# C Is Not a Low-level Language

**URL:** <https://fortran-lang.discourse.group/t/c-is-not-a-low-level-language/2464>\
**Category:** Uncategorized\
**Created:** [December 29, 2021, 2:26pm UTC](https://fortran-lang.discourse.group/t/c-is-not-a-low-level-language/2464 "2021-12-29T14:26:54Z")\
**Posts on this page:** 1\
**Showing post:** 25

<div class="post-metadata">

**Author:** ![ivanpribec](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/ivanpribec/32/3290_2.png) [@ivanpribec](https://fortran-lang.discourse.group/u/ivanpribec)\
**Post date:** [August 13, 2023, 11:22pm UTC](https://fortran-lang.discourse.group/t/c-is-not-a-low-level-language/2464/25 "2023-08-13T23:22:27Z")

</div>

> [@RonShepard](#):
>
> Isn’t there also an issue involving aliases?

There is, and indeed it can lead more efficient code. But this applies irrespective of OpenMP, which I mentioned in my original post.

The aliasing issue can be overcome in C with the standard `restrict` type qualifier (since C99). Here’s an example ([Compiler Explorer](https://godbolt.org/z/scjWcETEx)), for an x86-64 processor:

```cpp
void multiply_scalar(float *a, float *b) {
    *b = 3.0f*(*a);
}

void multiply_stream(float *a, float *b) {
    for (int i = 0; i < 4; i++)
        b[i] = 3.0f*a[i];
}

void multiply_stream_noalias(float * restrict a, float * restrict b) 
{
    for (int i = 0; i < 4; i++)
        b[i] = 3.0f*a[i];
}

```

```assembly
multiply_scalar(float*, float*):
        vmovss xmm0, DWORD PTR .LC0[rip]
        vmulss xmm0, xmm0, DWORD PTR [rdi]
        vmovss DWORD PTR [rsi], xmm0
        ret
multiply_stream(float*, float*):
        vmovss xmm1, DWORD PTR .LC0[rip]
        xor eax, eax
.L4:
        vmulss xmm0, xmm1, DWORD PTR [rdi+rax]
        vmovss DWORD PTR [rsi+rax], xmm0
        add rax, 4
        cmp rax, 16
        jne .L4
        ret
multiply_stream_noalias(float*, float*):
        vbroadcastss xmm0, DWORD PTR .LC0[rip]
        vmulps xmm0, xmm0, XMMWORD PTR [rdi] 
        vmovups XMMWORD PTR [rsi], xmm0
        ret
.LC0:
        .long 1077936128

```

As you can see, in `multiply_stream` there is a loop implementing scalar multiplication (`vmulss`). Once the `restrict` qualifier is added (or `__restrict` extension in C++), the compiler performs 4 multiplications in parallel with the `vmulps` instruction. The `xmm` registers are part of the [Streaming SIMD Extension](https://en.wikipedia.org/wiki/Streaming_SIMD_Extensions) (SSE). They are 128-bit wide, meaning they can fit four floats, or two doubles. When you multiply in scalar mode, you are essentially wasting three out of four SIMD lanes.

(Note, at `-O3` optimization some C and C++ compilers may insert runtime checks for aliasing with separate vectorized and scalar codepaths depending on the aliasing result.)

In Fortran, because of the default aliasing rules, the vector instruction is produced directly:

```fortran
subroutine multiply_stream(a,b)
   real, intent(in) :: a(4)
   real, intent(out) :: b(4)
   do i = 1, 4
    b(i) = 3.*a(i)
   end do
end subroutine

```

```assembly
multiply_stream_:
        vbroadcastss xmm0, DWORD PTR .LC0[rip]
        vmulps xmm0, xmm0, XMMWORD PTR [rdi]
        vmovups XMMWORD PTR [rsi], xmm0
        ret
.LC0:
        .long 1077936128

```

Addendum: using [Intel Intrinsics](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html) (SSE), the function to multiply four floats with a constant value can be written as,

```cpp
// Multiply elements of a by 3 and store result in b
void multiply_sse(float *a, float *b) {
    __m128 r = _mm_set_ps1(3.0f); // Broadcast 3.0 to all elements of r
    r = _mm_mul_ps (r, _mm_loadu_ps(a)); // Multiply packed single-precision operands
                                         // (for second operand unaligned load is used)
    _mm_storeu_ps(b, r); // Store result in b
}

```

---

_[View the full topic](https://fortran-lang.discourse.group/t/c-is-not-a-low-level-language/2464)._
