# Noalias and nested pointers

**URL:** <https://ziggit.dev/t/noalias-and-nested-pointers/9822>\
**Category:** Help\
**Tags:** language\
**Created:** [April 26, 2025, 11:46am UTC](https://ziggit.dev/t/noalias-and-nested-pointers/9822 "2025-04-26T11:46:06Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![Justus2308](https://ziggit.dev/user_avatar/ziggit.dev/justus2308/32/5305_2.png) [@Justus2308](https://ziggit.dev/u/Justus2308)\
**Post date:** [April 26, 2025, 11:46am UTC](https://ziggit.dev/t/noalias-and-nested-pointers/9822/1 "2025-04-26T11:46:06Z")

</div>

Hey, I am trying to understand how `noalias` works and need some clarification.  
The current Zig documentation sadly isn’t very helpful:

> The noalias keyword.  
> TODO add documentation for noalias

The LLVM docs state the following about [noalias](https://llvm.org/docs/LangRef.html#parameter-attributes):

> This indicates that memory locations accessed via pointer values _based_ on the argument or return value are not also accessed, during the execution of the function, via pointer values not _based_ on the argument or return value. This guarantee only holds for memory locations that are _modified_, by any means, during the execution of the function.

and about [pointer aliasing](https://llvm.org/docs/LangRef.html#pointeraliasing):

> A pointer value is associated with the addresses associated with any value it is _based_ on.

> A pointer value formed from a scalar `getelementptr` operation is _based_ on the pointer-typed operand of the `getelementptr`.  
> The pointer in lane _l_ of the result of a vector `getelementptr` operation is _based_ on the pointer in lane _l_ of the vector-of-pointers-typed operand of the `getelementptr`.

This is where I am a little confused. In contrast to C `restrict`, Zig `noalias` is per-parameter, not per-pointer.  
For example what’s happening here?

```zig
export fn aliasing(noalias ptr: *const [3]*[16]u64) u64 {
    var res: u64 = 0;
    for (ptr) |p| {
        for (p) |i| {
            res += i;
        }
    }
    return res;
}

```

Does `noalias` only cover the actual pointer values pointed to by ptr or does it also cover the `u64` these pointers point to recusively? Are the `u64` arrays still _based_ on `ptr`?

Looking at the LLVM IR:

```llvm
define dso_local i64 @aliasing(ptr noalias nocapture nonnull readonly align 8 %0) local_unnamed_addr {
Entry:
  %1 = load ptr, ptr %0, align 8
  %2 = load <16 x i64>, ptr %1, align 8
  %3 = getelementptr inbounds nuw i8, ptr %0, i64 8
  %4 = load ptr, ptr %3, align 8
  %5 = getelementptr inbounds nuw i8, ptr %0, i64 16
  %6 = load ptr, ptr %5, align 8
  %7 = load <16 x i64>, ptr %4, align 8
  %8 = load <16 x i64>, ptr %6, align 8
  [...]

```

are the `load <16 x i64>` instructions still _based_ on `ptr`?  
In other words, is `noalias` applied recursively to nested pointers, as if they were all marked `restrict`, or only to the top level pointer?

---

<div class="post-metadata">

**Author:** ![IntegratedQuantum](https://ziggit.dev/user_avatar/ziggit.dev/integratedquantum/32/782_2.png) [@IntegratedQuantum](https://ziggit.dev/u/IntegratedQuantum)\
**Post date:** [April 26, 2025, 12:12pm UTC](https://ziggit.dev/t/noalias-and-nested-pointers/9822/2 "2025-04-26T12:12:29Z")

</div>

I tried this on [godbolt](https://godbolt.org/z/EcsTqsnha) and it seems that `noalias` only applies to the top-level pointer, since it only optimizes away the top level pointer access in my example case.

---

<div class="post-metadata">

**Author:** ![Justus2308](https://ziggit.dev/user_avatar/ziggit.dev/justus2308/32/5305_2.png) [@Justus2308](https://ziggit.dev/u/Justus2308)\
**Post date:** [April 26, 2025, 12:37pm UTC](https://ziggit.dev/t/noalias-and-nested-pointers/9822/3 "2025-04-26T12:37:38Z")

</div>

Yeah, seems like it. Thanks for the example! Interestingly marking every pointer level as restricted in equivalent C code doesn’t seem to change that ([godbolt](https://godbolt.org/#z:OYLghAFBqd5TKALEBjA9gEwKYFFMCWALugE4A0BIEAZgQDbYB2AhgLbYgDkAjF%2BTXRMiAZVQtGIHgBYBQogFUAztgAKAD24AGfgCsp5eiyagAXgWDkVjVEQJDqzTAGF09AK5smIAEzknADIETNgAcp4ARtikIAAc5AAO6ErE9kyuHl6%2BicmpQkEh4WxRMfHW2LZpIkQspEQZnt5%2B5ZVC1bVEBWGR0XFWNXUNWc0DncHdxb2xAJRW6O6kqJxc2OpJdQDUNExb6OgQLCAbAFSn7jwAbNMb5xcbAKQA7ABC91oAghtfGywAdMf/f4PADMABENlp7sDXh9vhtSNgiAsdn8AWioTD3k9QW93qt1kQtjtBOgfBAmOgJAQWEofkdTsdbtdbg8Xri4ajAccQeDIdD2d8EUjSCiuf8MbjsVxZvRuABWfjeLg6cjobgALQsGyU80W2AePmBfHIRG00tmAGtfHLDNxpPw2CA5VpyEqVWquPwlCAXabldLyHBYCgMDh8MQyJRqHRGKwONxjYJhGIJJwZHJhMo1Jp/eR9H4jCYQAB9UuoKzYGx2BwQJxDJr%2BJiYLpFEogWRJFLV9JuRrZTt5JgtnoxWQtbvtQa94YVqtVUbDyaj/odevZJQL8at3rSWY6hZLbjuJgpYAhTDapBkQkkiDH0/ny/Xk6nFjXJ6YuEMlg8iES2GCoiyIvscLD/lijw4h8uL3hYj5KFemwkmSsFntgF4Ic%2BDIIhupAELYPzvmyAFfN%2Bv58p%2BgHCjs37gdikofKh8GITeezAneJ5wehT6bMcOFEHhBF8dguH4YSwmiQRb6spRpGvuR4FwkKwG0fy0GQQGspcAqrpmqq3DONqupLAawI%2BPwfo6NMsxINgLA4DEEAynaDpOi6br8B6Xo%2BiaZrWeQVrAsCvxBaFYXhRctpcMCip6V5vn%2BrMQYIPAEAhugbAJAw0RRhAGCZdlMSmAJx4WgIDBENE3oQBEekRMEtQAJ4Jvw9WsKQjUAPIRLoFR%2Bsa%2BUcMInVMPQzW5jgETuMAzgSPQ3q8PwOBsMYwCSBNBAIpUABuIl6asFTuJVLWUMIlZ6fQBARKQTWuDgekCQQjqLeQu2kBEyTYKC2ArSYl1FolAhGMASgAGoENgADunUJMwJ1JqI4iSOmCNZhoen6DwhirSA5iWJdETepAszoAk3YLQAtJ1GwAEqVnZKgAGI0kQnlvXhOBE05s59WkjhNmuWOBFuI4GAO3aCzkXZpIubZY%2BO86rtO3jy/TrRMJOYyFKL8ujJLG4dLLvQ8Huxlps52mxbmHobHjGwlUwFobBA4YkKQpkmxZfmWiAQUheFAdBZFWn2uQjrOrp1vcN5vp%2BYGiBpWgGVZYwFBUHlyeFWgqCFsAPAAJw8C6MaVaQ1W1bmbVNSdVcdd1vW2Cdg3MEQI1jXpk3TbN9DzSdy2retKqEFtdi7QtKoHagR3LMawSVVpKoEzdHV3csKqPc9xpvR9Kjfb9a3BKAgM0MDYMQ9DsNKom8gpsjsioyo6O5vm2NFqWxblgTXMk2TaSU51wINgU22OgCmkkiAUypDSQ%2BbNogcz2sTHm6t%2BYuGVgYYW2slxi1yBLNBWNxYyxFlg1Wc42h6zwUgicm5MFyxXFOTIKs6FawmHLU2B5OA%2BAtjpDy%2BkuAbHULEC4FMLjSA2DnVaGwC6/B4L8LQztXZkFMpwr2iUbJ2QctQC2odw7uTitHKwPlLLmgCr7YKgdA7B24DFSO7p9FGKSgnBOKBNq5S4MAikFNCBKHEKQTAFNtoSHcNgCmcYRJcD0vlFO0RQjsGWHbB2Folo/QHsEpQjVihuHHvwdCEYOYGBLtESBRBHpTUqlk8q9BS4U2bsIcpBTSCeOwFNYAFNgiCD0vU1pJ4SrqwphwGomAWA1A6TfJGaZ77yDRjmFUmNKF81rALChGCWHGyloOSWBD8hENoQrMhSsGEGF2RrahKyYi632X2c5dQjZnNmAJbA2ACCYB8pDG6CQWpcKtrYrgAQAigwALIbAAJI0w2ODKG0RbZagSfIwgbsPbkA2K4AqqcPbTBUVZQMKB5hEASEdXKkTCoxPjFwO2WhpE%2BFkdkuFZAnn5NGamKQEzMyP2mXoAwryWDvMWp8mxnluCdSOniwk6AaBQuAPbUgpVnbIqie7e4hpPYJSsj7P25iA6WK4NotyfLeEx2VQGZKicXHpzcSAzxBBvG1D8QEjwwTQlKHCbmQlqdiXLAEUIkRYjc6SPztIql5B%2B5/VSekj6vdcw5LdnSvwnShklOnnUiqhSalEETZUwpncWltI9CqTpwRcLuF6f0%2ByQzDi5gRrfcZGZFCsoxuuNW3YUGS2WduM5azcEHPwTgwhNDVlHM1vrBtitrnbL7eQztTCblSDuQiR5zyXScu5XwXlPCPS/IBcC0F4LIaQo9cI0R4iTC%2Bv9XIl2NL5WGj8EizOqKFVmXRQa/ytl7K9G5iHVyEdV36O9LHVRJi1XqrCpq6xX7PSPotuZXV8V7GzDeikBw0ggA%3D)) (and Zig seems to assume `-fno-strict-aliasing` by default).

---

<div class="post-metadata">

**Author:** ![LucasSantos91](https://ziggit.dev/user_avatar/ziggit.dev/lucassantos91/32/440_2.png) [@LucasSantos91](https://ziggit.dev/u/LucasSantos91)\
**Post date:** [April 26, 2025, 5:11pm UTC](https://ziggit.dev/t/noalias-and-nested-pointers/9822/4 "2025-04-26T17:11:13Z")

</div>

`noalias` is LLVM’s implementation of the restrict qualifier of C99. And that qualifier applies to any memory pointed by the pointer, regardless of the depth of the access.

From [cppreference](https://en.cppreference.com/w/c/language/restrict):

> During each execution of a block in which a restricted pointer P is declared (typically each execution of a function body in which P is a function parameter), if some object that is accessible through P (directly or indirectly) is modified, by any means, then all accesses to that object (both reads and writes) in that block must occur through P (directly or indirectly), otherwise the behavior is undefined:

I think what happened in @IntegratedQuantum 's snippet was just a missed optimization. It would still be undefined to modify memory that is overlapped by both parameters.

---

<div class="post-metadata">

**Author:** ![Justus2308](https://ziggit.dev/user_avatar/ziggit.dev/justus2308/32/5305_2.png) [@Justus2308](https://ziggit.dev/u/Justus2308)\
**Post date:** [April 26, 2025, 8:43pm UTC](https://ziggit.dev/t/noalias-and-nested-pointers/9822/5 "2025-04-26T20:43:15Z")

</div>

Thanks for the clarification!
