New professional programming language

Started by
24 comments, last by MrJoshL 10 years, 9 months ago

Hi game developers,

I've invented a new programming language based on over 20 years of experience writing games for a living and I'd like
to invite you all to give it a try.

The language is called C-UP, which is a play on the classic gaming terminology for lives/turns/power-ups: 1-up, 2-up, etc.

It's a C style imperative language so it should look very familiar to most of you but I hope it introduces enough new
concepts to make it worthwhile. I guess the headline features are safe automatic parallelism, full support for
SIMD vector types, proper arrays with slicing, multi-methods and generics, but there's lots of other good stuff.

Vector math is all SIMD based with intrinsic functions for things like dot, cross, min, max, sin, cos, etc.
There are libraries for collections, matrices, quaternions, window management, OpenGL, OpenCL, input, file system and
3d rigid body dynamics. Audio and networking are at the top of my list of libraries to implement next although you are
free to make your own.

There's also a debugger written in C-UP itself.

You can download it for free (Windows only) at: www.c-up.net.

Sorry for the basic website implementation but all of my time is being devoted to the language itself.

Unfortunately this means that there are no forums or anything yet but you can email me at the address given on the site
and of course discuss here. I'll try to answer any questions you have but it is just me and I do have a full time job in the
industry as well so please bear with me.

Thanks in advance for any time you're able to spend looking at this project - I hope you like it,

Si



P.S. Here's a little C-UP program to give you a taste of the language. This example draws some 2d coloured squares in parallel
using simd vector maths and 2d arrays with slicing.


// entry point
int Main(string[] args)
{
    // allocate a 2d dynamic array of 4 byte simd vector type
    // (a comprehensive range of simd types is supported)
    byte4[,] image = new byte4[64, 64];

    parallel
    {
        // fill the top-left quarter in red (array slicing with .. allows us to make a sub-array aliased over the master array)
        BlendRect(image[0..32, 0..32], byte4(255, 0, 0, 255));

        // fill the top-right quarter in green
        BlendRect(image[0..32, 32..64], byte4(0, 255, 0, 255));

	// fill the bottom-left quarter in blue
        BlendRect(image[32..64, 0..32], byte4(0, 0, 255, 255));

        // fill the bottom-right quarter in yellow
        BlendRect(image[32..64, 32..64], byte4(255, 255, 0, 255));

        // blend a 50% alpha white square over the middle
        BlendRect(image[16..48, 16..48], byte4(255, 255, 255, 128));

	// The calls to the parallel BlendRect function don't execute until the parallel block { } exits
        // and then they execute in parallel with automatic dependency checking.

        // In this example the first 4 squares will be filled in parallel and the final one will not happen until
        // they are all complete because it overlaps them all
    }

    return 0;
}

// fill an alpha blended rectangle
parallel void BlendRect(byte4[,] local rect, byte4 colour)
{
    // expand source colour to 16 bits per channel
    short4 srcColour = colour;

    // foreach on a 2d array gives each row in turn as a 1d array
    foreach (byte4[] row; rect)
    {
        // foreach on a 1d array yields each element in turn
        foreach (byte4& pixel; row)
        {
            // expand destination colour to 16 bits per channel
            short4 dstColour = *pixel;

            // perform blend - vector component swizzling and write masking is supported like glsl
            pixel.rgb = cast(byte4)(dstColour + ((srcColour - dstColour) * srcColour.aaaa) >> 8);
        }
    }
}
Advertisement

Certainly impressive work. What are you eventual plans regarding the language, will it become a commercial product, or do you plan to open-source it? If it remains as Windows-only, binary-only freeware, I'd assume people will be hesitant to invest time into using it, no matter how useful the actual language features are.

Thanks for the kind words and you make a good point.

It's always been and still is my intention to open source the entire thing it when it reaches a stable condition.
I do kind of feel that I'm still finding too many bugs for that to be a good idea right now.
I would also hope to have runtimes for Mac, Linux and at a stretch Android (with ARM back-end) by the end of the year.

Having said all that, I'm very keen for people to use it and if you think that just putting the source code out there regardless
of bugs would help to make that happen then I'll consider it.


Please let me know if you have any thoughts about how I should proceed.

Thanks,

Si

C-UP does not have the -> operator for dereferencing pointers, you always use the dot operator. This is because there’s no syntactic or semantic necessity to differentiate the two and doing so just makes refactoring code more difficult.

This right here. I like it.

"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"

My journals: dustArtemis ECS framework and Making a Terrain Generator

// In this example the first 4 squares will be filled in parallel and the final one will not happen until
// they are all complete because it overlaps them all

I find that feature very interesting! Does this basically mean, that when scheduling that parallel block, you can determine every byte that the function-calls in the block could possibly touch? That seems like a tricky thing for a compiler to figure out; how robust is it - can you fool it with pointer math or too much indirection?

How do you wait on these parallel jobs to complete? Does the closing brace act as a "join" point, or do you again automatically detect when a later function reads from their output regions and insert a 'wait' marker at that point?

Also, there may be some cases where you want to pass the same output array to every thread, but you know algorithmically that those threads will write to unpredictable but unique regions of the array. e.g. with a parallel radix sort, each thread will read from a non-overlapping range of the input array, but will write to non-overlapping, but also non-contiguous elements in the output array. Is there a way to kick off these kinds of "dangerous" parallel function calls (with undefined behaviour if the user is wrong about their algorithmic safety)?

Having said all that, I'm very keen for people to use it and if you think that just putting the source code out there regardless
of bugs would help to make that happen then I'll consider it. Please let me know if you have any thoughts about how I should proceed.

I recently decided to start using the ispc language, alongside C++ in my game, which was a big decision to commit to! Swaying users to a new language is no easy feat. Some of the things that swayed me were:

* The source code is published, so if it's abandoned by the authors, it's development can still continue. This is quite different to the author saying that they will publish the source at some point in the future.

* They use LLVM for back-end code generation, which reduces the complexity of the project and gives me a bit more faith in the general ability for the project to be maintained.

* The compiler spits out standard C object files, conforming to C's ABI, so I can integrate it into my project very easily, and do easy interop between ispc/c/c++.

* The compiler can also spit out C++ source files (transcode from ispc->c++), so if I want to port my code to a platform that isn't yet supported, I've got this backup plan available.

* The compiler spits out standard debug files to go with the object files, so existing debuggers can be used -- this one is only theoretical for me for now though, because this feature is Linux/Mac only at the moment sad.png

So yeah, I think that publishing the compiler as an open-source project would be a positive thing to do smile.png

Hi Hodgman. Yes it can determine every byte the the parallel function calls can modify. This is because it only allows modification via what I call local references. Simply put, local references can only exist in local scopes which fundamentally limits the scope of the data graph that can be accessed for writing (there's a much more detailed explanation in the docs.) That's not to say that the data you're modifying has to be local, but the references to it are tagged as local (e.g. "byte[] local" in the example above.)

The example doesn't show it but you can also declare that a particular set of types and/or memory heaps (it supports multiple heaps) are to be treated as constant during a particular parallel function call and any data that is one of these types or can be statically proven to be on one of these heaps can be freely accessed in parallel code but not modified.

This system is perfectly robust to my knowledge and one of my greatest fears in life is that someone will find that it isn't.

Yes, the end of the parallel scope acts as a join point.

You can't interfere with this stuff using threads because I don't allow you to use threads. There's a big rant at the end of the user guide about why I think threads are a terrible idea which might very well be a bone of contention. Anyway, you'd have to write your parallel radix sort using the parallel features of the language. There is support for shared memory though if you're an advanced user which allows you to manage access to memory yourself in a way that bypassed the dependency checking. The parallel collections provided with the language use this feature themselves and I guess you could do your sort example the same way.

Thanks for your points about swaying people to use the language - they are well taken. I know I wouldn't commit to someone elses language lightly. I'm going to have a serious think about the best way to get this into a releasable state. Hopefully I won't get hit by a bus in the meantime, but working in London you never know...

By the way, you mentioned interop with C which is easy because C-UP generates native code that can call C APIs natively. Please see the OpenGL and OpenCL bindings and sample provided if you need persuading :-)

So it seems the right thing to do is to release full source code which I'll do from the next version to give me a chance to tidy things up

a bit and separate out the platform dependent stuff better.

Here's a roadmap of my plans for the rest of this year.

1. Simple optimiser (done)

2. Fully test SSE4 back end and half floats

3. Remove DirectInput - use Windows raw input instead (done)

4. Require explicit type on generic parameters so you can overload generics on more than just number of generic parameters

5. Audio library

6. Sockets library

- v0.3 release with source code (guessing mid June)

7. Vectors of longs (not all that useful but good preparation for 9)

8. Scatter/gather array access (see bottom of user guide for loose specification)

9. Vectors of pointers with scatter/gather/downcast

10. Shift vector by vector

- v0.4 release

11. Raspberry Pi implementation (Linux runtime, ARM backend)

- v0.5 release

12. x64 runtime and back-end

13. AVX/AVX2 support (buy new PC - find out if AVX2 gather is worth it!)

14. Improved optimiser

- v0.6 release

Ah yes, because it's a language and an "engine" / runtime combined, you certainly have your work cut out for you :)

It's great to see you plan to open source it. In the best case you might find solid contributors or even co-maintainers for each platform, therefore greatly accelerating the progress.

You're not wrong about having my work cut out but I've been doing it for 4 years and have no intention of stopping now.

What a massive undertaking this must have been, you've done a great job so far. I'm currently playing with it, some things I noticed:

  • The compiler out flag seems to ignore letter case
  • First time I opened the debugger I was like wow, this sort of looks like an IDE. I was unable to view the shader files though, might be neat to add that.
  • I tried opening the debugger without specifying a cue file, the error I got wasn't so graceful tongue.png
    
    Unhandled exception: Access violation
    At:
    Unknown
    int Main(string[]) (line 1369) : module Main
    int* .Invoke(Delegate_47150,void*[]) (line 1298) : module Main
    void* Invoke(const(Function)&,const(void&)[] local) (line 1038) : module Reflection
    void anon-func_39(void*) (line 406) : module Program
    void FiberEntry(Fiber.EntryPoint) (line 102) : module Fiber
    Unknown
    Unknown
    Unknown
    

This topic is closed to new replies.

Advertisement