Skip to content

Commit 124ae14

Browse files
authored
Merge pull request #199 from sbillig/ptr-casts
bunch o' small optimizations
2 parents 1b6be0b + 80cb65e commit 124ae14

Some content is hidden

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

50 files changed

+13088
-1960
lines changed
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
use cranelift_entity::SecondaryMap;
2+
use smallvec::SmallVec;
3+
use sonatina_ir::{
4+
Function, InstSetExt, Type, ValueId,
5+
inst::evm::inst_set::EvmInstKind,
6+
isa::{Isa, evm::Evm},
7+
module::ModuleCtx,
8+
};
9+
10+
use crate::bitset::BitSet;
11+
12+
#[derive(Clone)]
13+
pub(crate) struct LateValueAliasMap {
14+
rep_of: SecondaryMap<ValueId, Option<ValueId>>,
15+
}
16+
17+
pub fn canonicalize_alias_value(
18+
value_aliases: &SecondaryMap<ValueId, Option<ValueId>>,
19+
value: ValueId,
20+
) -> ValueId {
21+
let mut current = value;
22+
loop {
23+
let next = value_aliases[current].unwrap_or(current);
24+
if next == current {
25+
return current;
26+
}
27+
current = next;
28+
}
29+
}
30+
31+
pub(crate) fn normalize_alias_map(
32+
function: &Function,
33+
value_aliases: &mut SecondaryMap<ValueId, Option<ValueId>>,
34+
) {
35+
for value in function.dfg.values.keys() {
36+
let mut seen: BitSet<ValueId> = BitSet::default();
37+
let mut path = SmallVec::<[ValueId; 8]>::new();
38+
let mut current = value;
39+
let mut rep = None;
40+
loop {
41+
if !seen.insert(current) {
42+
// Invalid alias cycles should not be canonicalized to an arbitrary value from
43+
// outside the cycle. Keep all traversed values self-canonical.
44+
for v in path.iter().copied() {
45+
value_aliases[v] = Some(v);
46+
}
47+
break;
48+
}
49+
path.push(current);
50+
let next = value_aliases[current].unwrap_or(current);
51+
if next == current {
52+
rep = Some(current);
53+
break;
54+
}
55+
current = next;
56+
}
57+
if let Some(rep) = rep {
58+
for v in path {
59+
value_aliases[v] = Some(rep);
60+
}
61+
}
62+
}
63+
64+
#[cfg(debug_assertions)]
65+
for value in function.dfg.values.keys() {
66+
let rep = value_aliases[value].unwrap_or(value);
67+
debug_assert_eq!(
68+
value_aliases[rep].unwrap_or(rep),
69+
rep,
70+
"value alias map is not one-hop canonical for v{}",
71+
value.as_u32()
72+
);
73+
}
74+
}
75+
76+
impl LateValueAliasMap {
77+
pub(crate) fn identity(function: &Function) -> Self {
78+
let mut rep_of: SecondaryMap<ValueId, Option<ValueId>> = SecondaryMap::new();
79+
for v in function.dfg.values.keys() {
80+
rep_of[v] = Some(v);
81+
}
82+
Self { rep_of }
83+
}
84+
85+
pub(crate) fn rep(&self, value: ValueId) -> ValueId {
86+
canonicalize_alias_value(&self.rep_of, value)
87+
}
88+
89+
pub(crate) fn map(&self) -> &SecondaryMap<ValueId, Option<ValueId>> {
90+
&self.rep_of
91+
}
92+
}
93+
94+
fn scalar_bit_width(ty: Type, module: &ModuleCtx) -> Option<u16> {
95+
let bits = match ty {
96+
Type::I1 => 1,
97+
Type::I8 => 8,
98+
Type::I16 => 16,
99+
Type::I32 => 32,
100+
Type::I64 => 64,
101+
Type::I128 => 128,
102+
Type::I256 => 256,
103+
Type::Unit => 0,
104+
Type::Compound(_) if ty.is_pointer(module) => 256,
105+
Type::Compound(_) => return None,
106+
};
107+
Some(bits)
108+
}
109+
110+
pub(crate) fn compute_evm_late_aliases(
111+
function: &Function,
112+
module: &ModuleCtx,
113+
isa: &Evm,
114+
) -> LateValueAliasMap {
115+
let mut aliases = LateValueAliasMap::identity(function);
116+
117+
for block in function.layout.iter_block() {
118+
for inst in function.layout.iter_inst(block) {
119+
let Some(result) = function.dfg.inst_result(inst) else {
120+
continue;
121+
};
122+
123+
let alias_from = match isa.inst_set().resolve_inst(function.dfg.inst(inst)) {
124+
EvmInstKind::Bitcast(bitcast) => Some(*bitcast.from()),
125+
EvmInstKind::IntToPtr(int_to_ptr) => {
126+
let src = *int_to_ptr.from();
127+
let src_bits = scalar_bit_width(function.dfg.value_ty(src), module);
128+
(src_bits == Some(256)).then_some(src)
129+
}
130+
EvmInstKind::PtrToInt(ptr_to_int) => {
131+
let dst_bits = scalar_bit_width(*ptr_to_int.ty(), module);
132+
(dst_bits == Some(256)).then_some(*ptr_to_int.from())
133+
}
134+
_ => None,
135+
};
136+
137+
let Some(from) = alias_from else {
138+
continue;
139+
};
140+
141+
let from_rep = aliases.rep(from);
142+
aliases.rep_of[result] = Some(from_rep);
143+
}
144+
}
145+
146+
aliases
147+
}
148+
149+
#[cfg(test)]
150+
mod tests {
151+
use super::*;
152+
use sonatina_parser::parse_module;
153+
use sonatina_triple::{Architecture, EvmVersion, OperatingSystem, TargetTriple, Vendor};
154+
155+
#[test]
156+
fn transitive_noop_cast_chain_collapses() {
157+
const SRC: &str = r#"
158+
target = "evm-ethereum-osaka"
159+
160+
func public %f(v0.i256, v1.*i8, v2.i64) {
161+
block0:
162+
v3.*i8 = int_to_ptr v0 *i8;
163+
v4.i256 = ptr_to_int v3 i256;
164+
v5.*i8 = bitcast v4 *i8;
165+
v6.i64 = ptr_to_int v1 i64;
166+
v7.*i8 = int_to_ptr v2 *i8;
167+
return;
168+
}
169+
"#;
170+
171+
let parsed = parse_module(SRC).expect("module parses");
172+
let func_ref = parsed
173+
.module
174+
.funcs()
175+
.into_iter()
176+
.find(|&f| parsed.module.ctx.func_sig(f, |sig| sig.name() == "f"))
177+
.expect("function exists");
178+
179+
let isa = Evm::new(TargetTriple {
180+
architecture: Architecture::Evm,
181+
vendor: Vendor::Ethereum,
182+
operating_system: OperatingSystem::Evm(EvmVersion::Osaka),
183+
});
184+
185+
parsed.module.func_store.view(func_ref, |function| {
186+
let aliases = compute_evm_late_aliases(function, &parsed.module.ctx, &isa);
187+
188+
let v = |name: &str| parsed.debug.value(func_ref, name).expect("value exists");
189+
190+
assert_eq!(aliases.rep(v("v3")), v("v0"));
191+
assert_eq!(aliases.rep(v("v4")), v("v0"));
192+
assert_eq!(aliases.rep(v("v5")), v("v0"));
193+
194+
// Non-noop width-changing casts must not alias.
195+
assert_eq!(aliases.rep(v("v6")), v("v6"));
196+
assert_eq!(aliases.rep(v("v7")), v("v7"));
197+
});
198+
}
199+
}

crates/codegen/src/isa/evm/memory_plan.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1104,7 +1104,9 @@ mod tests {
11041104
dom.compute(&cfg);
11051105

11061106
let block_order = dom.rpo().to_owned();
1107-
let alloc = StackifyAlloc::for_function(function, &cfg, &dom, &liveness, 16);
1107+
let alloc =
1108+
crate::stackalloc::StackifyBuilder::new(function, &cfg, &dom, &liveness, 16)
1109+
.compute();
11081110

11091111
analyses.insert(
11101112
func,

0 commit comments

Comments
 (0)