← Back to context

Comment by IshKebab

3 days ago

Everyone is not wrong, they just don't mean the straw man that you are taking down. The fact that there are integer, float, vector registers etc. does not invalidate the point that people mean when they say "assembly is untyped".

Assembly language itself is very strongly typed, because the types of the operands for any instruction are enforced in hardware by the CPU.

However, most assemblers do not help in any way the programmer with this, because they do implicit conversions between any data types, for the values stored in memory or in registers, or used as immediate operands.

This is only caused by a historical tradition. It would not be a problem to implement an assembler that strongly enforces the use of the right data types and which allows only a minimum of non-dangerous implicit data type conversions.

  • I'm not sure what you mean. From the hardware's point of view data loaded from memory is just bytes. You can happily store a float to memory and read it back as an int. Hardware doesn't care and neither do assemblers. And there's no practical way you could write an assembler that would care.

    • Exactly, if I write a Rust function which is actually wrapping the Intel ADD integer addition on 64-bit registers but I give my function floating point types (f64) instead, the CPU merrily performs the integer addition even though that's "wrong" in some sense.

      I don't have Bill's brand new nightly Odin compiler with "assembly templates" but I don't really see any useful way it could "fix" this. The machine does not care what your values "mean" to you, that's a human idea and that's what types are for.

      4 replies →

    • I mean that every instruction has operands that have well defined data types, exactly like any function of a high-level programming language.

      Most assemblers do not allow the programmer to declare the data types of the variables, so they do not check whether the types of the operands are valid.

      Nonetheless, nothing would stop someone to modify an assembler to require the declaration of the data types and to generate assembly errors whenever there is a type mismatch.

      The fact that if you give to an instruction that expects a floating-point number an integer operand then the CPU will compute a bogus value, is irrelevant.

      The same will happen if you give operands of the wrong type to any function of a program written in a high-level language.

      Any decent modern compiler will prevent you to use the wrong types, but if you use some tricks, you can still avoid this and invoke a function with wrong argument types, in which case it will compute some gibberish.

      So the parameters of HLL functions or procedures have well defined data types and the same is true for the operands of CPU instructions.

      Enforcing the use of the correct types can be done only at compile-time/assembly time, by the compiler or assembler.

      This happens because for efficiency reasons the primitive data types do not contain a tag for identifying their type. Only the programmer-defined union types contain a type tag, so that their type can be identified at run-time. In the languages without static type checking, but with dynamic type checking, any value belongs actually to a kind of tagged union type that includes all the types that can be used in that language, so the type of any argument can be determined at run time, but this is what makes such languages slow.

      In conclusion, there is no real difference between an assembly language and a HLL with static type checking, except that most people who have written assemblers were lazy and they did not implement type checking.

      The first C compilers never checked the types of the function arguments. Today this would no longer be an acceptable for a C compiler. For the same reasons, it should be no longer acceptable for any assembler to not check the data types.

      It is likely that the fact that no well-known assembler does an appropriate type checking is due to the necessity of defining a much more ample type system for an assembly language than for most high-level languages, so this would be a lot of work. Because the assembly programmers were used to lax assemblers anyway, implementing such a feature was not considered as a priority.

  • If you consider each possible encoding as a different instruction, CPU instructions don't even have operands, the register is part of the instruction. The CPU doesn't care what is in the register because only bits can be in the register and all instructions operate on bits.