Mill Computing, Inc. Forums The Mill Tools Compilers C semantics and the Mill

  • Author
    Posts
  • PeterH
    Participant
    Post count: 33
    #1753 |

    I’ve been thinking about C programming semantics and the Mill. Specifically how the Mill would support semantics that the C language I’m familiar with doesn’t. Any thoughts on an extended C that could fully support this architecture?

    A C function returns a single value. A Mill function can return many. A syntax like
    (a, b, c) = func(…);
    would be a nice language addition.

    The Mill supports vectors. A type qualifier is needed for this.

    With register or stack parameter passing the caller and subroutine need to agree on the type of a parameter. On the belt a parameter could be a scalar or vector of various size and it will still pass correctly. Problems may still arise if mismatched parameters are input to an operation. Being able to reflect on the width and cardinality of a belt value may be of value.

  • Will_Edwards
    Moderator
    Post count: 97

    I agree that C functions returning structs just to return multiple results is ugly. Syntactic sugar would help everyone I think. But that’s really a compiler thing and not a chip thing.

    Its useful to consider the two ways we use vectors on a CPU – long and short. This blog post does a good job explaining the distinction. The Mill, of course, supports both.

    So at a C level you may find yourself decorating short vectors explicitly (using the extensions compilers already offer) and we’ll work in the compiler back-ends to make sure that works much as it does on other CPUs. But we also make it easy for the compiler to do auto-vectorization and to pipeline a lot more than on other architectures.

    So to unlock the full power of the Mill you don’t need to rewrite your C programs – normal C maps great to the Mill (which is no accident).

    Modern compilers are aligning on how to extend with type, function and variable attributes so I could imagine we expose saturating and excepting arithmetic as well as some of the Mill security mechanisms that way. This won’t preclude people using toolchains where the frontend doesn’t know that that the target is a Mill, though, of course.

  • Ivan Godard
    Keymaster
    Post count: 460

    We have thought a bit about how some Mill facilities could be reached using language extensions. We will of course supply an intrinsics library for all the Mill opset, but intrinsics are a poor medium for coding.

    The Mill ability to return multiple results can be expressed in C++ using tuples, which may in time be adopted by C in some form. However, I personally find the C++ tuple syntax singularly unsatisfying. An alternative is to admit pass-by-copy for arguments, as exists in Ada and other languages in which function arguments may be marked as in, out, or inout. It would not be difficult for a compiler to implement out and inout arguments using multi-return. The notation should be much more convenient to use than picking tuples apart, while the execution should be more performant (and much safer) than using pointer or reference arguments.

    Will mentions the Mill overflow behaviors. These are most naturally brought into C as type-qualifiers: “__saturating short a” and the like. However, there would need to be promotion rules and extensions to the type lattice to deal with mixed arithmetic.

    There’s one thing you suggest that we cannot do, though. While the Mill operations are polymorphic at issue, they are not so at retire: the latency of (most) operations varies with the actual widths of the operands. Consequently it is not in general possible to write a function that is polymorphic over different types. Well, it can be done, sometimes, if you know exactly what you are doing – but the code will break on the next Mill member with a different timing matrix.

    We do intend to extend our own C compiler (and others) to better support Millisms, and will put in proposals to the language committees for the extensions. Thereafter it’s up to the language mavens ๐Ÿ™‚

    • PeterH
      Participant
      Post count: 33

      While the Mill operations are polymorphic at issue, they are not so at retire: the latency of (most) operations varies with the actual widths of the operands.

      Which means the specializer needs to know the size of the operands. I was thinking otherwise. Simplifies a consideration for division.

      A security consideration: when calling a service what prevents a parameter of the wrong size being passed and dropping a value on the wrong location on the belt? Is an exception thrown if operand sizes mismatch?

      • Ivan Godard
        Keymaster
        Post count: 460

        NYF; sorry ๐Ÿ™

  • Findecanor
    Participant
    Post count: 6

    I hope you don’t mind if I continue on this thread despite it being a bit old. I thought it was fitting here since it is about C semantics.

    I was looking recently at what C has “undefined”. One of these is when a bitcount for a shift is larger than the width of the value being shifted.
    If I interpret the Wiki correctly, a shift instruction’s result would be marked “NaR” (Not a Result) if the bitcount (from a belt operand) was too large — even if the instruction is not one of the excepting instructions.
    Did I get this right?

    Could the NaR flag be discarded (not throw a CPU exception) and code continue using an erroneous result — and what would that be?

    I agree that C functions returning structs just to return multiple results is ugly. Syntactic sugar would help everyone I think. But thatโ€™s really a compiler thing and not a chip thing.

    In Apple’s Swift programming language, multiple return values from functions are structs — from the viewpoint of the LLVM-based parts of the compiler.
    However.. the calling conventions used by Swift on ARM and x86-64 do specify that small structs be passed as multiple values in registers.
    The Mill’s compiler’s calling convention for C could be similar but for belt positions instead of registers.

    • Ivan Godard
      Keymaster
      Post count: 460

      We welcome questions!

      Overshift is implementation defined in C because different ISEs (that are too widely used to ignore handle) it differently. For us, the easy case is the excepting form “shiftlx”, which will get you an overshift NaR. The saturating form “shiftls” is also simple, as it saturates rather than overflows. Nearly as simple is the widening form “shiftlw” which will give you a double width result if the count is within that double width. However, if it is also not within the double width then you will get a non-NaR zero. And plain “shiftl” also overshifts to a non-NaR zero too.

      There is no single Mill op that uses the approach in which the count is modulo-divided to the operand size such that shift by 32 and shift by zero are synonymous (for word-width data). If you want those semantics you must explicitly mask the shift by an AND.

  • Findecanor
    Participant
    Post count: 6

    Thanks. I think that that behaviour is what most C programmers subconsciously expect out of the “<<” operator.
    You mentioned the cost of “or-trees” in another thread. I suppose that the same or-tree used for saturating shifts is also used for the other shifts.
    Putting an “and” before a “shiftlu” would be less instructions than the other way around for sure.

    BTW, don’t widening instructions always widen? What about vector ops that would drop two elements on the belt?

    • Ivan Godard
      Keymaster
      Post count: 460

      The widening op forms always widen, and when applied to vectors they drop two vectors each of half the number of elements.

You must be logged in to reply to this topic.