← Back to context

Comment by IshKebab

2 days ago

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.

  • Actually that case might be caught, depending on the architecture. E.g. on RISC-V float and integer registers are separate and the compiler would fail if you tell it you want an integer register and you try to load that with a float.

    That's his "aha, they are typed!" gotcha, but it's really not what anyone was talking about. And anyway, even in that case it isn't guaranteed - RISC-V has an optional configuration where float and integer registers are the same.

    • I was aware that some architectures had distinct floating point registers but I didn't know RISC-V is / could be one of them. As RISC-V becomes more popular perhaps I should consider choosing a different example for where typing evaporates to produce zero machine code during compilation, today I'd cite f32::to_bits which takes a 32-bit floating point value and gives us a 32-bit unsigned integer but maybe I should use u32::cast_signed since having distinct registers for signed versus unsigned integers is surely rare.

      2 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.