aboutsummaryrefslogtreecommitdiff

Amalgam

Amalgam is written in c89 C standard to work on as many devices as possible and with many different compilers, which would allow you to compile amalgam with a compiler that generates smaller (static) binaries than gcc.

Amalgam is not meant to be a replacement for any other language but rather a new unique language for programming with gpu without writing an external gpu program (glsl/hlsl).

Files have to be in utf-8 format and can optionally have utf-8 BOM.

Example

Hello world example:

const io = @import("std/io.amal");

const main = fn {
    io.stdout.write("hello, world!\n");
}

Important

Amalgam is not ready to be used yet.

Fast compilation

Every stage of the compiler runs concurrently, each compiling different files. Memory copying is kept to a minimal, for example tokenization is done without storing tokens in a list. Almost all allocation is done using an arena allocator that is only cleaned up once (when the compiler is finished), and the data is allocated sequentially. Locks are only used in one place in the whole compilation stage, during @import statements.

Dependencies

Right now only the C standard library (C89) is required for a release build. In the future dependency on the C standard library might be removed and then amalgam would have 0 dependencies.
python3 is needed for tests to run additional code analyzis.

Limits

Amalgam places limits on code for performance reasons. These are the limits:

  • One file can't have more than 254 imports, have more than 2^16 functions or use more than 2^16 functions.
  • Every function can only use up to 2^16 registers and parameters (combined).
  • Every function can only have up to 128 parameters, 128 return values and accept 128 arguments.
  • Exported and external function can only have 0 or 1 return values, as that is what C supports.

TODO

  • Build with -nostdlib and replace use of libc with syscalls (on linux).
  • Don't parse files unless the variable they are assigned to (with @import) is used. This is useful when only using small parts of a library. This could be done checking if an AST expression is referenced before evaluating it. There would then need to be a compile option that compiles everything even if not referenced, since another user of the program/library may use the functions that are not used in your program and they might have compile-issues.
  • Align machine code to word boundary for the start of functions. No need to pad with NOP, as functions return before the padding.
  • Use const to cleanup ANSI C style variable declarations, since const allows you to declare and assign variables on the same line.
  • Make the bytecode work with big endian. On a big endian machine, the bytecode should be converted to little endian to make work on little endian as little as possible, meaning it would be a small penality to use big endian.
  • Verify all members of an extern struct are extern as well. Verify all parameters are of extern types for extern functions.
  • Verify all code execution paths in a function return a value, if the function excepts return values.
  • Show compile error if the result of a function call is ignored.
  • Show compile error if variables are assigned to but not used.
  • Push arguments in reverse order (right-to-left, cdecl) (in program.c, since on windows we will need to support stdcall which is left-to-right).
  • After tokenizing files, unload file data to disk if possible.
  • Parallelize program decoding, if there is an advantage to doing it.
  • Logical AND binop should skip expressions the moment the result becomes false.
  • Reorder functions and imports by how they are used. Functions and imports that are used in some files should be in the bytecode before the files that they are used from, to reduce deferred function calls.
  • Document all limits.
  • To remove some overhead that external variables have on LhsExpr, make a ExternLhsExpr type.
  • Make function calls work for functions that return no value or returns multiple values.
  • Some bytecode instructions take 3 operands. These should be optimized to operate directly on the destination if the second or third operand is also the destination.
  • Align the instruction at the start of a function and the start of an expensive loop to 16-bytes. This improves performance and the block might even fit inside instruction cache.
    From intel instruction manual:
3.4.1.5 - Assembly/Compiler Coding Rule 12. (M impact, H generality)
All branch targets should be 16-byte aligned.
  • Reduce the number of branches by making memory allocation/reallocation throw instead of returning error.
  • Implement pub for imports and show compile error when using non-pub symbols in pub closures/structs/etc.
  • Use a list instead of a hash map for variable lookup, since the list will be small most of the time.
  • Optimize buffer_append by returning pre-allocated space which can be used directly, instead of adding data to a variable and then copying it to the buffer.
  • Use instructions such as CMPXCHG8B to optimize operations such as if(a == b) a = c;.
  • Do not allow (re)assigning to variables in global scope. This wouldn't be consistent since files are parsed asynchronously so the order wouldn't be deterministic.
  • Compile error if a variable has the same name as a keyword.
  • Only allow importing files that has .amal extension, this also fixes the issue where windows has illegal filenames such as aux, nul, etc. Also do the same for the main file.
  • Generate optimization report, where the code should be annotated with patterns the compiler sees and optimizes. This would help the user write code that the compiler can optimize.
  • Handle a situation similar to this: https://github.com/ziglang/zig/issues/4021.
  • Change the size of pthreads with setrlimit.
  • Handle EINTR.
  • Use semaphore instead of mutex to unlock other thread.
  • Check redefinition of variables, parameters and member variables/functions.

Documents

Documents are located under doc. The file doc/Documentation.md is generated from source files by running doc/doc_extract.py but there is no need to run this script unless you are modifying documentation in the source.

Development

Define AMAL_MUTEX_DEBUG in src/thread.c to enable mutex debugging.
Set PEDANTIC environment variable to 1 before compiling to enable pedantic mode.
Set SANITIZE_ADDRESS environment variable to 1 before compiling to run compile with asan.
Set SANITIZE_THREAD environment variable to 1 before compiling to run compile with tsan.
Set SCAN_BUILD environment variable to 1 before compiling to run scan-build on the source files.