diff --git a/Cargo.lock b/Cargo.lock index df04ae3..9885d2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -110,6 +110,12 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + [[package]] name = "hbbytecode" version = "0.1.0" @@ -123,7 +129,7 @@ name = "hblang" version = "0.1.0" dependencies = [ "env_logger", - "hashbrown", + "hashbrown 0.15.0", "hbbytecode", "hbvm", "log", @@ -191,7 +197,7 @@ source = "git+https://github.com/jakubDoka/regalloc2?branch=reuse-allocations#41 dependencies = [ "allocator-api2", "bumpalo", - "hashbrown", + "hashbrown 0.14.5", "log", "rustc-hash", "smallvec", diff --git a/hblang/Cargo.toml b/hblang/Cargo.toml index 01c24f8..284e397 100644 --- a/hblang/Cargo.toml +++ b/hblang/Cargo.toml @@ -8,7 +8,7 @@ name = "hbc" path = "src/main.rs" [dependencies] -hashbrown = { version = "0.14.5", default-features = false } +hashbrown = { version = "0.15.0", default-features = false, features = ["raw-entry"] } hbbytecode = { version = "0.1.0", path = "../hbbytecode" } hbvm = { path = "../hbvm", features = ["nightly"] } log = { version = "0.4.22", features = ["release_max_level_error"] } diff --git a/hblang/src/codegen.rs b/hblang/src/codegen.rs index fb58703..1c57640 100644 --- a/hblang/src/codegen.rs +++ b/hblang/src/codegen.rs @@ -747,10 +747,10 @@ impl Codegen { return ty.expand().inner(); } - let prev_tmp = self.tys.fields_tmp.len(); + let prev_tmp = self.tys.tmp.fields.len(); for sf in fields.iter().filter_map(CommentOr::or) { - let f = Field { name: self.tys.field_names.intern(sf.name), ty: self.ty(&sf.ty) }; - self.tys.fields_tmp.push(f); + let f = Field { name: self.tys.names.intern(sf.name), ty: self.ty(&sf.ty) }; + self.tys.tmp.fields.push(f); } self.tys.structs.push(Struct { field_start: self.tys.fields.len() as _, @@ -758,7 +758,7 @@ impl Codegen { file, ..Default::default() }); - self.tys.fields.extend(self.tys.fields_tmp.drain(prev_tmp..)); + self.tys.fields.extend(self.tys.tmp.fields.drain(prev_tmp..)); if let Some(sym) = sym { self.tys @@ -2580,7 +2580,7 @@ impl Codegen { self.ci.free_loc(ret.loc); - Global { ty: ret.ty, file, name, data, ast: ExprRef::new(expr), ..Default::default() } + Global { ty: ret.ty, file, name, data, ..Default::default() } } fn ct_eval( diff --git a/hblang/src/lib.rs b/hblang/src/lib.rs index d10598e..f320164 100644 --- a/hblang/src/lib.rs +++ b/hblang/src/lib.rs @@ -36,7 +36,7 @@ use { ty::ArrayLen, }, alloc::{collections::BTreeMap, string::String, vec::Vec}, - core::{cell::Cell, fmt::Display, hash::BuildHasher, ops::Range, usize}, + core::{cell::Cell, fmt::Display, ops::Range}, hashbrown::hash_map, hbbytecode as instrs, }; @@ -69,6 +69,89 @@ pub mod son; mod lexer; mod vc; +mod ctx_map { + use core::hash::BuildHasher; + + pub type Hash = u64; + pub type HashBuilder = core::hash::BuildHasherDefault; + + #[derive(Default)] + pub struct IdentityHasher(u64); + + impl core::hash::Hasher for IdentityHasher { + fn finish(&self) -> u64 { + self.0 + } + + fn write(&mut self, _: &[u8]) { + unimplemented!() + } + + fn write_u64(&mut self, i: u64) { + self.0 = i; + } + } + + pub struct Key { + pub value: T, + pub hash: Hash, + } + + impl core::hash::Hash for Key { + fn hash(&self, state: &mut H) { + state.write_u64(self.hash); + } + } + + pub trait CtxEntry { + type Ctx: ?Sized; + type Key<'a>: Eq + core::hash::Hash; + + fn key<'a>(&self, ctx: &'a Self::Ctx) -> Self::Key<'a>; + } + + pub struct CtxMap { + inner: hashbrown::HashMap, (), HashBuilder>, + } + + impl Default for CtxMap { + fn default() -> Self { + Self { inner: Default::default() } + } + } + + impl CtxMap { + pub fn entry<'a, 'b>( + &'a mut self, + value: T::Key<'b>, + ctx: &'b T::Ctx, + ) -> (hashbrown::hash_map::RawEntryMut<'a, Key, (), HashBuilder>, Hash) { + let hash = crate::FnvBuildHasher::default().hash_one(&value); + (self.inner.raw_entry_mut().from_hash(hash, |k| k.value.key(ctx) == value), hash) + } + + pub fn get<'a>(&self, value: T::Key<'a>, ctx: &'a T::Ctx) -> Option<&T> { + let hash = crate::FnvBuildHasher::default().hash_one(&value); + self.inner + .raw_entry() + .from_hash(hash, |k| k.value.key(ctx) == value) + .map(|(k, _)| &k.value) + } + + pub fn clear(&mut self) { + self.inner.clear(); + } + + pub fn remove<'a>(&mut self, value: &T, ctx: &'a T::Ctx) -> Option { + let (entry, _) = self.entry(value.key(ctx), ctx); + match entry { + hashbrown::hash_map::RawEntryMut::Occupied(o) => Some(o.remove_entry().0.value), + hashbrown::hash_map::RawEntryMut::Vacant(_) => None, + } + } + } +} + mod task { use super::Offset; @@ -124,7 +207,7 @@ mod ty { lexer::TokenKind, parser::{self}, }, - core::{num::NonZeroU32, ops::Range, usize}, + core::{num::NonZeroU32, ops::Range}, }; pub type ArrayLen = u32; @@ -153,10 +236,6 @@ mod ty { Some(Self((pos << Self::LEN_BITS | len) as u32)) } - //pub fn view(self, slice: &[Id]) -> &[Id] { - // &slice[self.0 as usize >> Self::LEN_BITS..][..self.len()] - //} - pub fn range(self) -> Range { let start = self.0 as usize >> Self::LEN_BITS; start..start + self.len() @@ -169,15 +248,20 @@ mod ty { pub fn empty() -> Self { Self(0) } - - pub fn repr(&self) -> u32 { - self.0 - } } #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] pub struct Id(NonZeroU32); + impl crate::ctx_map::CtxEntry for Id { + type Ctx = TypeInsts; + type Key<'a> = crate::SymKey<'a>; + + fn key<'a>(&self, ctx: &'a Self::Ctx) -> Self::Key<'a> { + todo!() + } + } + impl Default for Id { fn default() -> Self { Self(unsafe { NonZeroU32::new_unchecked(UNDECLARED) }) @@ -399,12 +483,7 @@ mod ty { if i != 0 { write!(f, ", ")?; } - write!( - f, - "{}: {}", - self.tys.field_names.ident_str(name), - self.rety(ty) - )?; + write!(f, "{}: {}", self.tys.names.ident_str(name), self.rety(ty))?; } write!(f, "}}") } else { @@ -442,15 +521,14 @@ fn emit(out: &mut Vec, (len, instr): EncodedInstr) { out.extend_from_slice(&instr[..len]); } -#[derive(PartialEq, Eq, Hash, Debug)] -enum SymKey { - Pointer(ty::Id), +#[derive(PartialEq, Eq, Hash)] +enum SymKey<'a> { + Pointer(&'a Ptr), Struct(FileId, Pos), FuncInst(ty::Func, ty::Tuple), MomizedCall(ty::Func), Decl(FileId, Ident), - Slice(ty::Id), - Array(ty::Id, ArrayLen), + Array(&'a Array), } #[derive(Clone, Copy)] @@ -491,7 +569,6 @@ struct Global { file: FileId, name: Ident, ty: ty::Id, - ast: ExprRef, offset: Offset, data: Vec, } @@ -502,7 +579,6 @@ impl Default for Global { ty: Default::default(), offset: u32::MAX, data: Default::default(), - ast: ExprRef::default(), file: u32::MAX, name: u32::MAX, } @@ -551,11 +627,12 @@ struct Struct { field_start: u32, } +#[derive(PartialEq, Eq, Hash)] struct Ptr { base: ty::Id, } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq, Eq, Hash)] struct Array { ty: ty::Id, len: ArrayLen, @@ -586,56 +663,30 @@ struct AbleOsExecutableHeader { metadata_length: u64, } -struct IdentEntry { - hash: u32, - ident: Ident, -} +impl ctx_map::CtxEntry for Ident { + type Ctx = str; + type Key<'a> = &'a str; -impl core::hash::Hash for IdentEntry { - fn hash(&self, state: &mut H) { - state.write_u64((self.hash as u64) << 32); - } -} - -#[derive(Default)] -struct IdentityHasher(u64); - -impl core::hash::Hasher for IdentityHasher { - fn finish(&self) -> u64 { - self.0 - } - - fn write(&mut self, _: &[u8]) { - unimplemented!() - } - - fn write_u64(&mut self, i: u64) { - self.0 = i; + fn key<'a>(&self, ctx: &'a Self::Ctx) -> Self::Key<'a> { + unsafe { ctx.get_unchecked(ident::range(*self)) } } } #[derive(Default)] struct IdentInterner { - lookup: hashbrown::HashMap>, + lookup: ctx_map::CtxMap, strings: String, } impl IdentInterner { fn intern(&mut self, ident: &str) -> Ident { - let hash = FnvBuildHasher::default().hash_one(ident) & 0xFFFFFFFF00000000; - match self.lookup.raw_entry_mut().from_hash( - hash, - |k| unsafe { self.strings.get_unchecked(ident::range(k.ident)) } == ident, - ) { - hash_map::RawEntryMut::Occupied(o) => o.get_key_value().0.ident, + let (entry, hash) = self.lookup.entry(ident, &self.strings); + match entry { + hash_map::RawEntryMut::Occupied(o) => o.get_key_value().0.value, hash_map::RawEntryMut::Vacant(v) => { let id = ident::new(self.strings.len() as _, ident.len() as _); self.strings.push_str(ident); - v.insert_hashed_nocheck( - hash, - IdentEntry { hash: (hash >> 32) as _, ident: id }, - (), - ); + v.insert(ctx_map::Key { hash, value: id }, ()); id } } @@ -646,33 +697,35 @@ impl IdentInterner { } fn project(&self, ident: &str) -> Option { - let hash = FnvBuildHasher::default().hash_one(ident) & 0xFFFFFFFF00000000; - self.lookup - .raw_entry() - .from_hash( - hash, - |k| unsafe { self.strings.get_unchecked(ident::range(k.ident)) } == ident, - ) - .map(|(k, _)| k.ident) + self.lookup.get(ident, &self.strings).copied() } } #[derive(Default)] -struct Types { - syms: HashMap, +struct TypesTmp { + fields: Vec, + frontier: Vec, + globals: Vec, + funcs: Vec, +} + +#[derive(Default)] +struct TypeIns { funcs: Vec, args: Vec, globals: Vec, structs: Vec, fields: Vec, - field_names: IdentInterner, ptrs: Vec, arrays: Vec, +} - fields_tmp: Vec, - frontier_tmp: Vec, - reachable_globals: Vec, - reachable_funcs: Vec, +#[derive(Default)] +struct Types { + syms: ctx_map::CtxMap, + names: IdentInterner, + ins: TypeIns, + tmp: TypesTmp, } const HEADER_SIZE: usize = core::mem::size_of::(); @@ -757,13 +810,13 @@ impl Types { return Some(ty); } - let prev_tmp = self.fields_tmp.len(); + let prev_tmp = self.tmp.fields.len(); for field in fields.iter().filter_map(CommentOr::or) { let Some(ty) = self.ty(file, &field.ty, files) else { - self.fields_tmp.truncate(prev_tmp); + self.tmp.fields.truncate(prev_tmp); return None; }; - self.fields_tmp.push(Field { name: self.field_names.intern(field.name), ty }); + self.tmp.fields.push(Field { name: self.names.intern(field.name), ty }); } self.structs.push(Struct { @@ -772,7 +825,7 @@ impl Types { explicit_alignment: packed.then_some(1), ..Default::default() }); - self.fields.extend(self.fields_tmp.drain(prev_tmp..)); + self.fields.extend(self.tmp.fields.drain(prev_tmp..)); let ty = ty::Kind::Struct(self.structs.len() as u32 - 1).compress(); self.syms.insert(sym, ty); @@ -794,12 +847,12 @@ impl Types { } fn dump_reachable(&mut self, from: ty::Func, to: &mut Vec) -> AbleOsExecutableHeader { - debug_assert!(self.frontier_tmp.is_empty()); - debug_assert!(self.reachable_funcs.is_empty()); - debug_assert!(self.reachable_globals.is_empty()); + debug_assert!(self.tmp.frontier.is_empty()); + debug_assert!(self.tmp.funcs.is_empty()); + debug_assert!(self.tmp.globals.is_empty()); - self.frontier_tmp.push(ty::Kind::Func(from).compress()); - while let Some(itm) = self.frontier_tmp.pop() { + self.tmp.frontier.push(ty::Kind::Func(from).compress()); + while let Some(itm) = self.tmp.frontier.pop() { match itm.expand() { ty::Kind::Func(func) => { let fuc = &mut self.funcs[func as usize]; @@ -807,8 +860,8 @@ impl Types { continue; } fuc.offset = 0; - self.reachable_funcs.push(func); - self.frontier_tmp.extend(fuc.relocs.iter().map(|r| r.target)); + self.tmp.funcs.push(func); + self.tmp.frontier.extend(fuc.relocs.iter().map(|r| r.target)); } ty::Kind::Global(glob) => { let glb = &mut self.globals[glob as usize]; @@ -816,13 +869,13 @@ impl Types { continue; } glb.offset = 0; - self.reachable_globals.push(glob); + self.tmp.globals.push(glob); } _ => unreachable!(), } } - for &func in &self.reachable_funcs { + for &func in &self.tmp.funcs { let fuc = &mut self.funcs[func as usize]; fuc.offset = to.len() as _; to.extend(&fuc.code); @@ -830,7 +883,7 @@ impl Types { let code_length = to.len(); - for global in self.reachable_globals.drain(..) { + for global in self.tmp.globals.drain(..) { let global = &mut self.globals[global as usize]; global.offset = to.len() as _; to.extend(&global.data); @@ -838,7 +891,7 @@ impl Types { let data_length = to.len() - code_length; - for func in self.reachable_funcs.drain(..) { + for func in self.tmp.funcs.drain(..) { let fuc = &self.funcs[func as usize]; for rel in &fuc.relocs { let offset = match rel.target.expand() { @@ -1003,7 +1056,7 @@ impl Types { } fn find_struct_field(&self, s: ty::Struct, name: &str) -> Option { - let name = self.field_names.project(name)?; + let name = self.names.project(name)?; self.struct_fields(s).iter().position(|f| f.name == name) } } @@ -1020,7 +1073,7 @@ impl OffsetIter { } fn offset_of(tys: &Types, idx: ty::Struct, field: &str) -> Option<(Offset, ty::Id)> { - let field_id = tys.field_names.project(field)?; + let field_id = tys.names.project(field)?; OffsetIter::new(idx, tys) .into_iter(tys) .find(|(f, _)| f.name == field_id) diff --git a/hblang/src/son.rs b/hblang/src/son.rs index c51ec03..7895fa2 100644 --- a/hblang/src/son.rs +++ b/hblang/src/son.rs @@ -1,5 +1,6 @@ use { crate::{ + ctx_map::CtxEntry, ident::Ident, instrs, lexer::{self, TokenKind}, @@ -11,7 +12,7 @@ use { task, ty::{self}, vc::{BitSet, Vc}, - Func, HashMap, IdentityHasher, Offset, OffsetIter, Reloc, Sig, SymKey, TypedReloc, Types, + Func, HashMap, Offset, OffsetIter, Reloc, Sig, SymKey, TypedReloc, Types, }, alloc::{borrow::ToOwned, string::String, vec::Vec}, core::{ @@ -20,12 +21,11 @@ use { convert::identity, fmt::{self, Debug, Display, Write}, format_args as fa, - hash::{BuildHasher, Hasher}, - mem, ops, + hash::BuildHasher, + mem, ops, usize, }, hashbrown::hash_map, regalloc2::VReg, - std::process::id, }; const VOID: Nid = 0; @@ -44,19 +44,17 @@ pub mod reg { pub type Reg = u8; } -struct LookupEntry { - nid: Nid, - hash: u64, -} +type Lookup = crate::ctx_map::CtxMap; -impl core::hash::Hash for LookupEntry { - fn hash(&self, state: &mut H) { - state.write_u64(self.hash); +impl crate::ctx_map::CtxEntry for Nid { + type Ctx = [Result]; + type Key<'a> = (Kind, &'a [Nid], ty::Id); + + fn key<'a>(&self, ctx: &'a Self::Ctx) -> Self::Key<'a> { + ctx[*self as usize].as_ref().unwrap().key() } } -type Lookup = hashbrown::HashMap>; - struct Nodes { values: Vec>, visited: BitSet, @@ -106,10 +104,10 @@ impl Nodes { let mut lookup_meta = None; if !node.is_lazy_phi() { - let (raw_entry, hash) = Self::find_node(&mut self.lookup, &self.values, &node); + let (raw_entry, hash) = self.lookup.entry(node.key(), &self.values); let entry = match raw_entry { - hash_map::RawEntryMut::Occupied(o) => return o.get_key_value().0.nid, + hash_map::RawEntryMut::Occupied(o) => return o.get_key_value().0.value, hash_map::RawEntryMut::Vacant(v) => v, }; @@ -129,38 +127,14 @@ impl Nodes { self.free = mem::replace(&mut self.values[free as usize], Ok(node)).unwrap_err(); if let Some((entry, hash)) = lookup_meta { - entry.insert(LookupEntry { nid: free, hash }, ()); + entry.insert(crate::ctx_map::Key { value: free, hash }, ()); } free } - fn find_node<'a>( - lookup: &'a mut Lookup, - values: &[Result], - node: &Node, - ) -> ( - hash_map::RawEntryMut<'a, LookupEntry, (), core::hash::BuildHasherDefault>, - u64, - ) { - let hash = crate::FnvBuildHasher::default().hash_one(node.key()); - let entry = lookup - .raw_entry_mut() - .from_hash(hash, |n| values[n.nid as usize].as_ref().unwrap().key() == node.key()); - (entry, hash) - } - fn remove_node_lookup(&mut self, target: Nid) { if !self[target].is_lazy_phi() { - match Self::find_node( - &mut self.lookup, - &self.values, - self.values[target as usize].as_ref().unwrap(), - ) - .0 - { - hash_map::RawEntryMut::Occupied(o) => o.remove(), - hash_map::RawEntryMut::Vacant(_) => unreachable!(), - }; + self.lookup.remove(&target, &self.values).unwrap(); } } @@ -379,20 +353,16 @@ impl Nodes { let prev = self[target].inputs[inp_index]; self[target].inputs[inp_index] = with; - let (entry, hash) = Self::find_node( - &mut self.lookup, - &self.values, - self.values[target as usize].as_ref().unwrap(), - ); + let (entry, hash) = self.lookup.entry(target.key(&self.values), &self.values); match entry { hash_map::RawEntryMut::Occupied(other) => { - let rpl = other.get_key_value().0.nid; + let rpl = other.get_key_value().0.value; self[target].inputs[inp_index] = prev; self.replace(target, rpl); rpl } hash_map::RawEntryMut::Vacant(slot) => { - slot.insert(LookupEntry { nid: target, hash }, ()); + slot.insert(crate::ctx_map::Key { value: target, hash }, ()); let index = self[prev].outputs.iter().position(|&o| o == target).unwrap(); self[prev].outputs.swap_remove(index); self[with].outputs.push(target); @@ -845,7 +815,7 @@ impl fmt::Display for Kind { #[derive(Debug, Default, Clone)] //#[repr(align(64))] -struct Node { +pub struct Node { kind: Kind, inputs: Vc, outputs: Vc, @@ -1144,7 +1114,7 @@ impl Codegen { .tys .struct_fields(s) .iter() - .map(|f| self.tys.field_names.ident_str(f.name)) + .map(|f| self.tys.names.ident_str(f.name)) .intersperse("', '") .collect::(); self.report( @@ -1374,7 +1344,7 @@ impl Codegen { .iter() .zip(offs) .filter(|&(_, (ty, _))| ty != ty::Id::UNDECLARED) - .map(|(f, _)| self.tys.field_names.ident_str(f.name)) + .map(|(f, _)| self.tys.names.ident_str(f.name)) .intersperse(", ") .collect::();