| 1 | //! Alias analysis — Fortran-specific oracle. |
| 2 | //! |
| 3 | //! Determines whether two memory pointers can refer to the same |
| 4 | //! storage location. Leverages Fortran's strong aliasing guarantees: |
| 5 | //! |
| 6 | //! - Distinct `Alloca` instructions → NoAlias (different stack slots) |
| 7 | //! - Distinct `GlobalAddr` names → NoAlias (Fortran no-alias guarantee) |
| 8 | //! - Distinct ordinary Fortran dummy arguments → NoAlias |
| 9 | //! - Same pointer value → MustAlias |
| 10 | //! - GEP from same base with different constant offsets → NoAlias |
| 11 | //! - Everything else → MayAlias (conservative) |
| 12 | //! |
| 13 | //! Used by GVN (to determine if a store kills a prior load's value) |
| 14 | //! cross-block load-store forwarding, and LICM load hoisting. |
| 15 | |
| 16 | use super::loop_utils::resolve_const_int; |
| 17 | use crate::ir::inst::*; |
| 18 | use crate::ir::types::IrType; |
| 19 | |
| 20 | /// Result of an alias query between two pointer values. |
| 21 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 22 | pub enum AliasResult { |
| 23 | /// The pointers definitely refer to the same location. |
| 24 | MustAlias, |
| 25 | /// The pointers might refer to the same location. |
| 26 | MayAlias, |
| 27 | /// The pointers definitely refer to different locations. |
| 28 | NoAlias, |
| 29 | } |
| 30 | |
| 31 | /// True if `entry_ptr` may be reachable through `call_arg` when |
| 32 | /// a call passes `call_arg` across a function boundary. |
| 33 | /// |
| 34 | /// At call boundaries the precise offset-based alias check is |
| 35 | /// unsound: the callee receives the pointer and can walk to any |
| 36 | /// offset within the same allocation. `query()` would say |
| 37 | /// `NoAlias` for `gep %0, [0]` vs `gep %0, [1]` because their |
| 38 | /// offsets differ, but a callee reading through the former can |
| 39 | /// still touch the latter. Load-store forwarding and DSE must |
| 40 | /// use this coarser predicate when reasoning about calls. |
| 41 | /// |
| 42 | /// The rule: if both pointers trace to the same underlying |
| 43 | /// allocation (same alloca, same global, same param slot), they |
| 44 | /// are considered call-reachable aliases regardless of offset. |
| 45 | /// Distinct allocations remain NoAlias by Fortran's strong |
| 46 | /// aliasing guarantee. |
| 47 | pub fn may_reach_through_call_arg(func: &Function, entry_ptr: ValueId, call_arg: ValueId) -> bool { |
| 48 | if entry_ptr == call_arg { |
| 49 | return true; |
| 50 | } |
| 51 | let base_entry = trace_base(func, entry_ptr); |
| 52 | let base_arg = trace_base(func, call_arg); |
| 53 | match (&base_entry, &base_arg) { |
| 54 | (PtrBase::Alloca(a), PtrBase::Alloca(b)) => a == b, |
| 55 | (PtrBase::Global(a), PtrBase::Global(b)) => a == b, |
| 56 | (PtrBase::Param(a), PtrBase::Param(b)) => { |
| 57 | if a == b { |
| 58 | return true; |
| 59 | } |
| 60 | !(param_is_fortran_noalias(func, *a) && param_is_fortran_noalias(func, *b)) |
| 61 | } |
| 62 | (PtrBase::Unknown, _) | (_, PtrBase::Unknown) => true, |
| 63 | // Distinct kinds of allocations never alias per Fortran. |
| 64 | _ => false, |
| 65 | } |
| 66 | } |
| 67 | |
| 68 | /// Query whether two pointer values may alias. |
| 69 | /// |
| 70 | /// Both `a` and `b` should be pointer-typed values (results of Alloca, |
| 71 | /// GlobalAddr, GetElementPtr, or function parameters). |
| 72 | pub fn query(func: &Function, a: ValueId, b: ValueId) -> AliasResult { |
| 73 | // Same value → must alias. |
| 74 | if a == b { |
| 75 | return AliasResult::MustAlias; |
| 76 | } |
| 77 | |
| 78 | // Trace both pointers to their base + offset. |
| 79 | let base_a = trace_base(func, a); |
| 80 | let base_b = trace_base(func, b); |
| 81 | |
| 82 | // Different base pointers → no alias (Fortran guarantee). |
| 83 | match (&base_a, &base_b) { |
| 84 | (PtrBase::Alloca(id_a), PtrBase::Alloca(id_b)) if id_a != id_b => { |
| 85 | return AliasResult::NoAlias; |
| 86 | } |
| 87 | (PtrBase::Global(name_a), PtrBase::Global(name_b)) if name_a != name_b => { |
| 88 | return AliasResult::NoAlias; |
| 89 | } |
| 90 | (PtrBase::Param(id_a), PtrBase::Param(id_b)) |
| 91 | if id_a != id_b |
| 92 | && param_is_fortran_noalias(func, *id_a) |
| 93 | && param_is_fortran_noalias(func, *id_b) => |
| 94 | { |
| 95 | return AliasResult::NoAlias; |
| 96 | } |
| 97 | (PtrBase::Alloca(_), PtrBase::Global(_)) |
| 98 | | (PtrBase::Global(_), PtrBase::Alloca(_)) |
| 99 | | (PtrBase::Alloca(_), PtrBase::Param(_)) |
| 100 | | (PtrBase::Param(_), PtrBase::Alloca(_)) => { |
| 101 | return AliasResult::NoAlias; |
| 102 | } |
| 103 | _ => {} |
| 104 | } |
| 105 | |
| 106 | // Same base, different constant offsets → no alias. |
| 107 | if base_a.base_id() == base_b.base_id() { |
| 108 | let off_a = trace_offset(func, a); |
| 109 | let off_b = trace_offset(func, b); |
| 110 | if let (Some(oa), Some(ob)) = (off_a, off_b) { |
| 111 | if oa != ob { |
| 112 | if pointer_points_to_aggregate(func, a) || pointer_points_to_aggregate(func, b) { |
| 113 | return AliasResult::MayAlias; |
| 114 | } |
| 115 | return AliasResult::NoAlias; |
| 116 | } |
| 117 | // Same base + same offset → must alias. |
| 118 | return AliasResult::MustAlias; |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | AliasResult::MayAlias |
| 123 | } |
| 124 | |
| 125 | fn pointer_points_to_aggregate(func: &Function, ptr: ValueId) -> bool { |
| 126 | matches!( |
| 127 | func.value_type(ptr), |
| 128 | Some(IrType::Ptr(inner)) if matches!(inner.as_ref(), IrType::Array(..) | IrType::Struct(_)) |
| 129 | ) |
| 130 | } |
| 131 | |
| 132 | /// Traced pointer base — the root allocation or global. |
| 133 | #[derive(Debug)] |
| 134 | enum PtrBase { |
| 135 | Alloca(ValueId), |
| 136 | Global(String), |
| 137 | Param(ValueId), |
| 138 | Unknown, |
| 139 | } |
| 140 | |
| 141 | impl PtrBase { |
| 142 | fn base_id(&self) -> Option<ValueId> { |
| 143 | match self { |
| 144 | PtrBase::Alloca(id) | PtrBase::Param(id) => Some(*id), |
| 145 | _ => None, |
| 146 | } |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | /// Trace a pointer value back to its base allocation. |
| 151 | fn trace_base(func: &Function, ptr: ValueId) -> PtrBase { |
| 152 | // Check if this is a function parameter (pointer arg). |
| 153 | for param in &func.params { |
| 154 | if param.id == ptr { |
| 155 | return PtrBase::Param(ptr); |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | // Find the defining instruction. |
| 160 | let Some(inst) = find_inst(func, ptr) else { |
| 161 | return PtrBase::Unknown; |
| 162 | }; |
| 163 | |
| 164 | match &inst.kind { |
| 165 | InstKind::Alloca(_) => PtrBase::Alloca(ptr), |
| 166 | InstKind::GlobalAddr(name) => PtrBase::Global(name.clone()), |
| 167 | InstKind::GetElementPtr(base, _) => trace_base(func, *base), |
| 168 | InstKind::Load(addr) => trace_param_wrapper(func, *addr) |
| 169 | .map(PtrBase::Param) |
| 170 | .unwrap_or(PtrBase::Unknown), |
| 171 | _ => PtrBase::Unknown, |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | /// Trace a pointer to its constant byte offset from the base, if possible. |
| 176 | fn trace_offset(func: &Function, ptr: ValueId) -> Option<i64> { |
| 177 | if func.params.iter().any(|param| param.id == ptr) { |
| 178 | return Some(0); |
| 179 | } |
| 180 | let inst = find_inst(func, ptr)?; |
| 181 | match &inst.kind { |
| 182 | InstKind::Alloca(_) | InstKind::GlobalAddr(_) => Some(0), |
| 183 | InstKind::Load(addr) => trace_param_wrapper(func, *addr).map(|_| 0), |
| 184 | InstKind::GetElementPtr(base, indices) => { |
| 185 | let base_offset = trace_offset(func, *base)?; |
| 186 | if indices.len() != 1 { |
| 187 | return None; |
| 188 | } |
| 189 | let idx = resolve_const_int(func, indices[0])?; |
| 190 | let step = match &inst.ty { |
| 191 | IrType::Ptr(inner) => inner.size_bytes() as i64, |
| 192 | _ => return None, |
| 193 | }; |
| 194 | Some(base_offset + idx * step) |
| 195 | } |
| 196 | _ => None, |
| 197 | } |
| 198 | } |
| 199 | |
| 200 | fn param_is_fortran_noalias(func: &Function, param_id: ValueId) -> bool { |
| 201 | func.params |
| 202 | .iter() |
| 203 | .find(|param| param.id == param_id) |
| 204 | .map(|param| param.fortran_noalias) |
| 205 | .unwrap_or(false) |
| 206 | } |
| 207 | |
| 208 | fn trace_param_wrapper(func: &Function, addr: ValueId) -> Option<ValueId> { |
| 209 | let slot = match find_inst(func, addr).map(|inst| &inst.kind) { |
| 210 | Some(InstKind::Alloca(_)) => addr, |
| 211 | Some(InstKind::GetElementPtr(base, _)) if trace_offset(func, addr)? == 0 => { |
| 212 | match trace_base(func, *base) { |
| 213 | PtrBase::Alloca(slot) => slot, |
| 214 | _ => return None, |
| 215 | } |
| 216 | } |
| 217 | _ => return None, |
| 218 | }; |
| 219 | |
| 220 | let mut stored_param = None; |
| 221 | for block in &func.blocks { |
| 222 | for inst in &block.insts { |
| 223 | let InstKind::Store(val, ptr) = &inst.kind else { |
| 224 | continue; |
| 225 | }; |
| 226 | if *ptr != slot { |
| 227 | continue; |
| 228 | } |
| 229 | if !func.params.iter().any(|param| param.id == *val) { |
| 230 | return None; |
| 231 | } |
| 232 | if stored_param.replace(*val).is_some() { |
| 233 | return None; |
| 234 | } |
| 235 | } |
| 236 | } |
| 237 | |
| 238 | stored_param |
| 239 | } |
| 240 | |
| 241 | fn find_inst(func: &Function, vid: ValueId) -> Option<&Inst> { |
| 242 | for block in &func.blocks { |
| 243 | for inst in &block.insts { |
| 244 | if inst.id == vid { |
| 245 | return Some(inst); |
| 246 | } |
| 247 | } |
| 248 | } |
| 249 | None |
| 250 | } |
| 251 | |
| 252 | // --------------------------------------------------------------------------- |
| 253 | // Tests |
| 254 | // --------------------------------------------------------------------------- |
| 255 | |
| 256 | #[cfg(test)] |
| 257 | mod tests { |
| 258 | use super::*; |
| 259 | use crate::ir::types::{IntWidth, IrType}; |
| 260 | use crate::lexer::{Position, Span}; |
| 261 | |
| 262 | fn span() -> Span { |
| 263 | let pos = Position { line: 0, col: 0 }; |
| 264 | Span { |
| 265 | file_id: 0, |
| 266 | start: pos, |
| 267 | end: pos, |
| 268 | } |
| 269 | } |
| 270 | |
| 271 | fn param(name: &str, id: u32, ty: IrType, fortran_noalias: bool) -> Param { |
| 272 | Param { |
| 273 | name: name.into(), |
| 274 | ty, |
| 275 | id: ValueId(id), |
| 276 | fortran_noalias, |
| 277 | } |
| 278 | } |
| 279 | |
| 280 | #[test] |
| 281 | fn distinct_allocas_no_alias() { |
| 282 | let mut f = Function::new("test".into(), vec![], IrType::Void); |
| 283 | let a = f.next_value_id(); |
| 284 | f.register_type(a, IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); |
| 285 | f.block_mut(f.entry).insts.push(Inst { |
| 286 | id: a, |
| 287 | ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 288 | span: span(), |
| 289 | kind: InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 290 | }); |
| 291 | let b = f.next_value_id(); |
| 292 | f.register_type(b, IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); |
| 293 | f.block_mut(f.entry).insts.push(Inst { |
| 294 | id: b, |
| 295 | ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 296 | span: span(), |
| 297 | kind: InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 298 | }); |
| 299 | f.block_mut(f.entry).terminator = Some(Terminator::Return(None)); |
| 300 | |
| 301 | assert_eq!(query(&f, a, b), AliasResult::NoAlias); |
| 302 | } |
| 303 | |
| 304 | #[test] |
| 305 | fn same_pointer_must_alias() { |
| 306 | let mut f = Function::new("test".into(), vec![], IrType::Void); |
| 307 | let a = f.next_value_id(); |
| 308 | f.register_type(a, IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); |
| 309 | f.block_mut(f.entry).insts.push(Inst { |
| 310 | id: a, |
| 311 | ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 312 | span: span(), |
| 313 | kind: InstKind::Alloca(IrType::Int(IntWidth::I32)), |
| 314 | }); |
| 315 | f.block_mut(f.entry).terminator = Some(Terminator::Return(None)); |
| 316 | |
| 317 | assert_eq!(query(&f, a, a), AliasResult::MustAlias); |
| 318 | } |
| 319 | |
| 320 | #[test] |
| 321 | fn mixed_width_geps_same_index_do_not_must_alias() { |
| 322 | let mut f = Function::new("test".into(), vec![], IrType::Void); |
| 323 | let base_ty = IrType::Array(Box::new(IrType::Int(IntWidth::I8)), 384); |
| 324 | let base = f.next_value_id(); |
| 325 | f.register_type(base, IrType::Ptr(Box::new(base_ty.clone()))); |
| 326 | f.block_mut(f.entry).insts.push(Inst { |
| 327 | id: base, |
| 328 | ty: IrType::Ptr(Box::new(base_ty)), |
| 329 | span: span(), |
| 330 | kind: InstKind::Alloca(IrType::Array(Box::new(IrType::Int(IntWidth::I8)), 384)), |
| 331 | }); |
| 332 | |
| 333 | let four = f.next_value_id(); |
| 334 | f.register_type(four, IrType::Int(IntWidth::I64)); |
| 335 | f.block_mut(f.entry).insts.push(Inst { |
| 336 | id: four, |
| 337 | ty: IrType::Int(IntWidth::I64), |
| 338 | span: span(), |
| 339 | kind: InstKind::ConstInt(4, IntWidth::I64), |
| 340 | }); |
| 341 | |
| 342 | let gep_i32 = f.next_value_id(); |
| 343 | f.register_type(gep_i32, IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); |
| 344 | f.block_mut(f.entry).insts.push(Inst { |
| 345 | id: gep_i32, |
| 346 | ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 347 | span: span(), |
| 348 | kind: InstKind::GetElementPtr(base, vec![four]), |
| 349 | }); |
| 350 | |
| 351 | let gep_i64 = f.next_value_id(); |
| 352 | f.register_type(gep_i64, IrType::Ptr(Box::new(IrType::Int(IntWidth::I64)))); |
| 353 | f.block_mut(f.entry).insts.push(Inst { |
| 354 | id: gep_i64, |
| 355 | ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I64))), |
| 356 | span: span(), |
| 357 | kind: InstKind::GetElementPtr(base, vec![four]), |
| 358 | }); |
| 359 | |
| 360 | f.block_mut(f.entry).terminator = Some(Terminator::Return(None)); |
| 361 | |
| 362 | assert_eq!(query(&f, gep_i32, gep_i64), AliasResult::NoAlias); |
| 363 | } |
| 364 | |
| 365 | #[test] |
| 366 | fn gep_different_offsets_no_alias() { |
| 367 | let mut f = Function::new("test".into(), vec![], IrType::Void); |
| 368 | let base = f.next_value_id(); |
| 369 | f.register_type(base, IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); |
| 370 | f.block_mut(f.entry).insts.push(Inst { |
| 371 | id: base, |
| 372 | ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 373 | span: span(), |
| 374 | kind: InstKind::Alloca(IrType::Array(Box::new(IrType::Int(IntWidth::I32)), 10)), |
| 375 | }); |
| 376 | // GEP at offset 0 |
| 377 | let c0 = f.next_value_id(); |
| 378 | f.register_type(c0, IrType::Int(IntWidth::I64)); |
| 379 | f.block_mut(f.entry).insts.push(Inst { |
| 380 | id: c0, |
| 381 | ty: IrType::Int(IntWidth::I64), |
| 382 | span: span(), |
| 383 | kind: InstKind::ConstInt(0, IntWidth::I64), |
| 384 | }); |
| 385 | let gep0 = f.next_value_id(); |
| 386 | f.register_type(gep0, IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); |
| 387 | f.block_mut(f.entry).insts.push(Inst { |
| 388 | id: gep0, |
| 389 | ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 390 | span: span(), |
| 391 | kind: InstKind::GetElementPtr(base, vec![c0]), |
| 392 | }); |
| 393 | // GEP at offset 1 |
| 394 | let c1 = f.next_value_id(); |
| 395 | f.register_type(c1, IrType::Int(IntWidth::I64)); |
| 396 | f.block_mut(f.entry).insts.push(Inst { |
| 397 | id: c1, |
| 398 | ty: IrType::Int(IntWidth::I64), |
| 399 | span: span(), |
| 400 | kind: InstKind::ConstInt(1, IntWidth::I64), |
| 401 | }); |
| 402 | let gep1 = f.next_value_id(); |
| 403 | f.register_type(gep1, IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); |
| 404 | f.block_mut(f.entry).insts.push(Inst { |
| 405 | id: gep1, |
| 406 | ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 407 | span: span(), |
| 408 | kind: InstKind::GetElementPtr(base, vec![c1]), |
| 409 | }); |
| 410 | f.block_mut(f.entry).terminator = Some(Terminator::Return(None)); |
| 411 | |
| 412 | assert_eq!(query(&f, gep0, gep1), AliasResult::NoAlias); |
| 413 | } |
| 414 | |
| 415 | #[test] |
| 416 | fn aggregate_base_and_element_gep_may_alias() { |
| 417 | let mut f = Function::new("test".into(), vec![], IrType::Void); |
| 418 | let base = f.next_value_id(); |
| 419 | f.register_type( |
| 420 | base, |
| 421 | IrType::Ptr(Box::new(IrType::Array( |
| 422 | Box::new(IrType::Int(IntWidth::I32)), |
| 423 | 10, |
| 424 | ))), |
| 425 | ); |
| 426 | f.block_mut(f.entry).insts.push(Inst { |
| 427 | id: base, |
| 428 | ty: IrType::Ptr(Box::new(IrType::Array( |
| 429 | Box::new(IrType::Int(IntWidth::I32)), |
| 430 | 10, |
| 431 | ))), |
| 432 | span: span(), |
| 433 | kind: InstKind::Alloca(IrType::Array(Box::new(IrType::Int(IntWidth::I32)), 10)), |
| 434 | }); |
| 435 | |
| 436 | let c1 = f.next_value_id(); |
| 437 | f.register_type(c1, IrType::Int(IntWidth::I64)); |
| 438 | f.block_mut(f.entry).insts.push(Inst { |
| 439 | id: c1, |
| 440 | ty: IrType::Int(IntWidth::I64), |
| 441 | span: span(), |
| 442 | kind: InstKind::ConstInt(1, IntWidth::I64), |
| 443 | }); |
| 444 | |
| 445 | let gep1 = f.next_value_id(); |
| 446 | f.register_type(gep1, IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); |
| 447 | f.block_mut(f.entry).insts.push(Inst { |
| 448 | id: gep1, |
| 449 | ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 450 | span: span(), |
| 451 | kind: InstKind::GetElementPtr(base, vec![c1]), |
| 452 | }); |
| 453 | f.block_mut(f.entry).terminator = Some(Terminator::Return(None)); |
| 454 | |
| 455 | assert_eq!(query(&f, base, gep1), AliasResult::MayAlias); |
| 456 | } |
| 457 | |
| 458 | #[test] |
| 459 | fn distinct_globals_no_alias() { |
| 460 | let mut f = Function::new("test".into(), vec![], IrType::Void); |
| 461 | let ga = f.next_value_id(); |
| 462 | f.register_type(ga, IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); |
| 463 | f.block_mut(f.entry).insts.push(Inst { |
| 464 | id: ga, |
| 465 | ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 466 | span: span(), |
| 467 | kind: InstKind::GlobalAddr("array_a".into()), |
| 468 | }); |
| 469 | let gb = f.next_value_id(); |
| 470 | f.register_type(gb, IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); |
| 471 | f.block_mut(f.entry).insts.push(Inst { |
| 472 | id: gb, |
| 473 | ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 474 | span: span(), |
| 475 | kind: InstKind::GlobalAddr("array_b".into()), |
| 476 | }); |
| 477 | f.block_mut(f.entry).terminator = Some(Terminator::Return(None)); |
| 478 | |
| 479 | assert_eq!(query(&f, ga, gb), AliasResult::NoAlias); |
| 480 | } |
| 481 | |
| 482 | #[test] |
| 483 | fn distinct_noalias_params_do_not_alias() { |
| 484 | let params = vec![ |
| 485 | param( |
| 486 | "a", |
| 487 | 0, |
| 488 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 489 | true, |
| 490 | ), |
| 491 | param( |
| 492 | "b", |
| 493 | 1, |
| 494 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 495 | true, |
| 496 | ), |
| 497 | ]; |
| 498 | let mut f = Function::new("test".into(), params, IrType::Void); |
| 499 | f.block_mut(f.entry).terminator = Some(Terminator::Return(None)); |
| 500 | |
| 501 | assert_eq!(query(&f, ValueId(0), ValueId(1)), AliasResult::NoAlias); |
| 502 | } |
| 503 | |
| 504 | #[test] |
| 505 | fn wrapper_loads_trace_back_to_noalias_params() { |
| 506 | let params = vec![ |
| 507 | param( |
| 508 | "a", |
| 509 | 0, |
| 510 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 511 | true, |
| 512 | ), |
| 513 | param( |
| 514 | "b", |
| 515 | 1, |
| 516 | IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 517 | true, |
| 518 | ), |
| 519 | ]; |
| 520 | let mut f = Function::new("test".into(), params, IrType::Void); |
| 521 | |
| 522 | let slot_a = f.next_value_id(); |
| 523 | f.register_type( |
| 524 | slot_a, |
| 525 | IrType::Ptr(Box::new(IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))))), |
| 526 | ); |
| 527 | f.block_mut(f.entry).insts.push(Inst { |
| 528 | id: slot_a, |
| 529 | ty: IrType::Ptr(Box::new(IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))))), |
| 530 | span: span(), |
| 531 | kind: InstKind::Alloca(IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))), |
| 532 | }); |
| 533 | let slot_b = f.next_value_id(); |
| 534 | f.register_type( |
| 535 | slot_b, |
| 536 | IrType::Ptr(Box::new(IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))))), |
| 537 | ); |
| 538 | f.block_mut(f.entry).insts.push(Inst { |
| 539 | id: slot_b, |
| 540 | ty: IrType::Ptr(Box::new(IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))))), |
| 541 | span: span(), |
| 542 | kind: InstKind::Alloca(IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))), |
| 543 | }); |
| 544 | let store_a = f.next_value_id(); |
| 545 | f.register_type(store_a, IrType::Void); |
| 546 | f.block_mut(f.entry).insts.push(Inst { |
| 547 | id: store_a, |
| 548 | ty: IrType::Void, |
| 549 | span: span(), |
| 550 | kind: InstKind::Store(ValueId(0), slot_a), |
| 551 | }); |
| 552 | let store_b = f.next_value_id(); |
| 553 | f.register_type(store_b, IrType::Void); |
| 554 | f.block_mut(f.entry).insts.push(Inst { |
| 555 | id: store_b, |
| 556 | ty: IrType::Void, |
| 557 | span: span(), |
| 558 | kind: InstKind::Store(ValueId(1), slot_b), |
| 559 | }); |
| 560 | let load_a = f.next_value_id(); |
| 561 | f.register_type(load_a, IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); |
| 562 | f.block_mut(f.entry).insts.push(Inst { |
| 563 | id: load_a, |
| 564 | ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 565 | span: span(), |
| 566 | kind: InstKind::Load(slot_a), |
| 567 | }); |
| 568 | let load_b = f.next_value_id(); |
| 569 | f.register_type(load_b, IrType::Ptr(Box::new(IrType::Int(IntWidth::I32)))); |
| 570 | f.block_mut(f.entry).insts.push(Inst { |
| 571 | id: load_b, |
| 572 | ty: IrType::Ptr(Box::new(IrType::Int(IntWidth::I32))), |
| 573 | span: span(), |
| 574 | kind: InstKind::Load(slot_b), |
| 575 | }); |
| 576 | f.block_mut(f.entry).terminator = Some(Terminator::Return(None)); |
| 577 | |
| 578 | assert_eq!(query(&f, load_a, load_b), AliasResult::NoAlias); |
| 579 | } |
| 580 | } |
| 581 |