Comment by torstenvl

5 hours ago

> C isn't not a high level assembler

Hmm.

The text following this heading seems to take the opposite view. I suspect this is a typo.

However, I think the heading is accurate as written. The "C is not a high level assembler" crowd, in my view, is making a category error, conflating C itself with an ISO standard and abstract machine concept coming decades later.

By the same token, "C is a high level assembler" is a gross oversimplification.

"C isn't not a high level assembler" indeed.

> The "C is not a high level assembler" crowd, in my view, is making a category error, conflating C itself with an ISO standard and abstract machine concept coming decades later.

"C isn't not a high level assembler" captures it almost perfectly, and it also captures the documented intent of the people who created the ANSI C standard, which was that ANSI C should not preclude the (common) use of C as a high-level assembly language.

Here the quote:

C code can be non-portable. Although it strove to give programmers the opportunity to write truly portable programs, the C89 Committee did not want to force programmers into writing portably, to preclude the use of C as a “high-level assembler”: the ability to write machine- specific code is one of the strengths of C. It is this principle which largely motivates drawing the distinction between strictly conforming program and conforming program (§4).

https://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.1...

(bottom of page 2)

What would you say C is beyond the high-level assembler ? genuinely curious (i kinda hold that belief personally but i'm not a c programmer by trade)

  • I see a huge semantic gap between assembly language and C.

    An assembly language program specifies a sequence of CPU instructions. The mapping between lines of code and generated instructions is one-to-one, or nearly so.

    A C program specifies run-time behavior, without regard to what CPU instructions might be used to achieve that.

    C is at a lower level than a lot of other languages, but it's not an assembly language.

    • And yet modern assembly does not correspond 1:1 to the micro-ops the CPU runs or even necessarily the order in which they run.

      Both ISA-level assembly and C are targeting an abstract machine model, even if the former is somewhat further removed from hardware reality.

      4 replies →

  • Look at the compiler assembly output after optimization kicks in. The resulting assembly code is usually significantly different from the input source code. With modern optimizer passes, C is much closer to any other high level programming language than to handwritten assembly code.

  • C is a programming language. It makes for a very shitty high level assembler.

    Here's a trivial example clang will often implement differently on different systems, producing two different results. Clang x64 will generally mul+add, while clang arm64 is aggressive about fma.

        x = 3.0f*x+1.0f;
    

    But that's just the broad strategy. Depending on the actual compiler flags, the assembly generated might include anything up to multiple function calls under the hood (sanitizers, soft floats, function profiling, etc).

    • I don't think clang is being "aggressive" on ARM, it's just that all aarch64 targets support fma. You'll get similar results with vfmadd213ss on x86-64 with -march=haswell (13 years old at this point, probably a safe bet).

          float fma(float x) {
              return 3.0f * x + 1.0f;
          }
      

      Clang armv8 21.1.0:

          fma(float):
              sub     sp, sp, #16
              str     s0, [sp, #12]
              ldr     s1, [sp, #12]
              fmov    s2, #1.00000000
              fmov    s0, #3.00000000
              fmadd   s0, s0, s1, s2
              add     sp, sp, #16
              ret
      

      Clang x86-64 21.1.0:

          .LCPI0_0:
              .long   0x3f800000
          .LCPI0_1:
              .long   0x40400000
          fma(float):
              push    rbp
              mov     rbp, rsp
              vmovss  dword ptr [rbp - 4], xmm0
              vmovss  xmm1, dword ptr [rbp - 4]
              vmovss  xmm2, dword ptr [rip + .LCPI0_0]
              vmovss  xmm0, dword ptr [rip + .LCPI0_1]
              vfmadd213ss     xmm0, xmm1, xmm2
              pop     rbp
              ret

      1 reply →

  • This was only the case when the machine code generated from C compilers was almost 1:1 to PDP-11, or similar 16 bit home computers.

    Since optmizing compilers became a thing in the C world, and the WG14 never considered modern CPU architectures on what hardware features C should expose, this idea lost meaning.

    However many people hold on to old beliefs that C is still the same kind of C that they learnt with the first edition of K&R C book.

    This ACM article might be interesting to you, https://queue.acm.org/detail.cfm?id=3212479

    Before dismissing it as the author having no idea what he is talking about, David Chisnall used to be a GCC contributor, one of the main GNUStep contributors back in the original days, and one of the key researchers behind the CHERI project.

    • David is quite accomplished, but in this instance he is simply wrong. For two reasons:

      1. All the reasons he cites that depend on "what the metal does" being different and quite a bit more complex than what is surfaced in C apply equally to machine/assembly language. So the CPU's instruction set is not a low-level language? Interesting take, but I don't think so: it is the lowest level language that is exposed by the CPU.

      2. The other reasons boil down to "I would like to do this optimization", and that is simply inapplicable.