├── tests ├── functions │ ├── main_expected.txt │ ├── variants_expected.txt │ ├── no_parameters_expected.txt │ ├── one_parameter_expected.txt │ ├── call_call_expected.txt │ ├── multiple_parameters_expected.txt │ ├── main.s │ ├── no_parameters.s │ ├── one_parameter.s │ ├── multiple_parameters.s │ ├── call_call.s │ └── variants.s ├── arrays │ ├── 1D_expected.txt │ ├── 3D_expected.txt │ ├── 1D.s │ └── 3D.s ├── branches │ ├── if_ret_expected.txt │ ├── else_expected.txt │ ├── else_if_expected.txt │ ├── if_else_ret_expected.txt │ ├── if_expected.txt │ ├── if_cast_expected.txt │ ├── if_ret.s │ ├── if.s │ ├── if_else_ret.s │ ├── if_cast.s │ ├── else.s │ └── else_if.s ├── casting │ ├── explicit_expected.txt │ └── explicit.s ├── structs │ ├── declaration_expected.txt │ ├── namespaces_expected.txt │ ├── variable_declaration_expected.txt │ ├── multiple_access_expected.txt │ ├── global_declaration_expected.txt │ ├── multiple_access_ptr_expected.txt │ ├── members_expected.txt │ ├── copy_expected.txt │ ├── pass_by_value_expected.txt │ ├── variable_declaration.s │ ├── declaration.s │ ├── multiple_access.s │ ├── multiple_access_ptr.s │ ├── global_declaration.s │ ├── copy.s │ ├── namespaces.s │ ├── pass_by_value.s │ └── members.s ├── pointers │ ├── declaration_expected.txt │ └── declaration.s ├── variables │ ├── declaration_expected.txt │ ├── negation_expected.txt │ ├── reassignment_expected.txt │ ├── declaration_assignment_expected.txt │ ├── declaration_reassignment_expected.txt │ ├── declaration.s │ ├── declaration_assignment.s │ ├── reassignment.s │ ├── declaration_reassignment.s │ └── negation.s ├── literals │ ├── bool_expected.txt │ ├── char_expected.txt │ ├── u8_expected.txt │ ├── i8_expected.txt │ ├── u16_expected.txt │ ├── string_expected.txt │ ├── i16_expected.txt │ ├── u32_expected.txt │ ├── i32_expected.txt │ ├── u64_expected.txt │ ├── i64_expected.txt │ ├── string.s │ ├── bool.s │ ├── char.s │ ├── u8.s │ ├── i8.s │ ├── u16.s │ ├── i16.s │ ├── u32.s │ ├── i32.s │ ├── u64.s │ └── i64.s ├── namespaces │ ├── deep_call_expected.txt │ ├── deep_declaration_expected.txt │ ├── shallow_call_expected.txt │ ├── shallow_declaration_expected.txt │ ├── shallow_declaration.s │ ├── shallow_call.s │ ├── deep_declaration.s │ └── deep_call.s ├── operators │ ├── predicate_expected.txt │ ├── logical_not_expected.txt │ ├── math_expected.txt │ ├── comparison_expected.txt │ ├── predicate.s │ ├── logical_not.s │ ├── math.s │ └── comparison.s └── random_keywords │ ├── alignof_expected.txt │ ├── sizeof_expected.txt │ ├── alignof.s │ └── sizeof.s ├── run_tests.bat ├── setup.bat ├── images ├── banner_1.png ├── banner_2.png └── banner_3.png ├── third_party └── premake │ ├── premake5.exe │ └── LICENSE.txt ├── source ├── intermediate_representation │ ├── codegen │ │ ├── memory │ │ │ ├── allocators │ │ │ │ ├── allocator_base.cpp │ │ │ │ ├── allocator_base.h │ │ │ │ └── linear_scan_allocator.h │ │ │ ├── memory.cpp │ │ │ └── memory.h │ │ ├── transformation │ │ │ ├── scheduler.h │ │ │ ├── live_range_analysis.h │ │ │ ├── use_list.h │ │ │ ├── transformation_context.h │ │ │ ├── use_list.cpp │ │ │ ├── live_range_analysis.cpp │ │ │ └── scheduler.cpp │ │ ├── optimization │ │ │ ├── optimization_pass_list.cpp │ │ │ └── optimization_pass_list.h │ │ ├── work_list.h │ │ ├── control_flow_graph.h │ │ ├── instruction.cpp │ │ ├── codegen_target.h │ │ ├── codegen_context.cpp │ │ ├── instruction_operand.h │ │ ├── live_interval.cpp │ │ ├── live_interval.h │ │ ├── instruction_operand.cpp │ │ ├── work_list.cpp │ │ ├── codegen_target.cpp │ │ ├── control_flow_graph.cpp │ │ ├── codegen_context.h │ │ └── instruction.h │ ├── node_hierarchy │ │ ├── user.h │ │ ├── properties │ │ │ ├── memory.h │ │ │ ├── types.h │ │ │ ├── operations.h │ │ │ └── control_flow.h │ │ ├── symbol.cpp │ │ ├── global.cpp │ │ ├── symbol.h │ │ ├── global.h │ │ ├── types.cpp │ │ └── types.h │ ├── module_output.cpp │ ├── target │ │ ├── parameter_descriptor.h │ │ ├── arch │ │ │ ├── architecture.h │ │ │ └── x64 │ │ │ │ └── x64_disassembler.h │ │ ├── target.h │ │ ├── target.cpp │ │ ├── outputs │ │ │ ├── object_file_emitter.h │ │ │ ├── object_file_emitter.cpp │ │ │ ├── elf.h │ │ │ └── coff.h │ │ └── system │ │ │ ├── systemv │ │ │ └── systemv.h │ │ │ └── win │ │ │ └── win.h │ ├── module_output.h │ └── module.h ├── compiler │ ├── test │ │ └── main.s │ ├── compiler │ │ ├── type_system │ │ │ ├── namespace_list.h │ │ │ ├── scope.cpp │ │ │ ├── namespace_list.cpp │ │ │ ├── scope.h │ │ │ ├── semantic_context.h │ │ │ └── type.h │ │ ├── compilation_context.h │ │ ├── compiler.h │ │ └── compiler.cpp │ └── main.cpp ├── ir_translator │ ├── values │ │ ├── function_registry.cpp │ │ ├── variable_registry.cpp │ │ ├── function_registry.h │ │ └── variable_registry.h │ └── ir_translator.h ├── abstract_syntax_tree │ ├── tree.cpp │ ├── tree.h │ ├── node.cpp │ └── node.h ├── tokenizer │ ├── token_buffer.h │ ├── token.h │ ├── token_buffer.cpp │ ├── tokenizer.h │ └── token.cpp └── type_checker │ └── type_checker.h ├── .gitmodules ├── .github └── workflows │ ├── windows.yml │ └── linux.yml ├── documents ├── CONTRIBUTING.md ├── COMPILER_DESIGN.md └── STYLE.md ├── LICENSE ├── .gitattributes └── README.md /tests/functions/main_expected.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/arrays/1D_expected.txt: -------------------------------------------------------------------------------- 1 | 1 2 2 | -------------------------------------------------------------------------------- /tests/arrays/3D_expected.txt: -------------------------------------------------------------------------------- 1 | 1 2 2 | -------------------------------------------------------------------------------- /tests/branches/if_ret_expected.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/casting/explicit_expected.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/functions/variants_expected.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/structs/declaration_expected.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/branches/else_expected.txt: -------------------------------------------------------------------------------- 1 | false 2 | -------------------------------------------------------------------------------- /tests/branches/else_if_expected.txt: -------------------------------------------------------------------------------- 1 | 3 2 | -------------------------------------------------------------------------------- /tests/branches/if_else_ret_expected.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/branches/if_expected.txt: -------------------------------------------------------------------------------- 1 | true 2 | -------------------------------------------------------------------------------- /tests/functions/no_parameters_expected.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/functions/one_parameter_expected.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/pointers/declaration_expected.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/variables/declaration_expected.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/branches/if_cast_expected.txt: -------------------------------------------------------------------------------- 1 | true 2 | -------------------------------------------------------------------------------- /tests/functions/call_call_expected.txt: -------------------------------------------------------------------------------- 1 | 12 2 | -------------------------------------------------------------------------------- /tests/functions/multiple_parameters_expected.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/literals/bool_expected.txt: -------------------------------------------------------------------------------- 1 | 1 0 2 | 1 0 3 | -------------------------------------------------------------------------------- /tests/literals/char_expected.txt: -------------------------------------------------------------------------------- 1 | x y 2 | m m 3 | -------------------------------------------------------------------------------- /tests/namespaces/deep_call_expected.txt: -------------------------------------------------------------------------------- 1 | 20 2 | -------------------------------------------------------------------------------- /tests/namespaces/deep_declaration_expected.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/structs/namespaces_expected.txt: -------------------------------------------------------------------------------- 1 | 111 2 | -------------------------------------------------------------------------------- /tests/structs/variable_declaration_expected.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /run_tests.bat: -------------------------------------------------------------------------------- 1 | .\output\tests\bin\Debug\tests.exe 2 | -------------------------------------------------------------------------------- /tests/literals/u8_expected.txt: -------------------------------------------------------------------------------- 1 | 0 255 2 | 0 255 3 | -------------------------------------------------------------------------------- /tests/namespaces/shallow_call_expected.txt: -------------------------------------------------------------------------------- 1 | 20 2 | -------------------------------------------------------------------------------- /tests/namespaces/shallow_declaration_expected.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/variables/negation_expected.txt: -------------------------------------------------------------------------------- 1 | 10 2 | -10 3 | -------------------------------------------------------------------------------- /tests/variables/reassignment_expected.txt: -------------------------------------------------------------------------------- 1 | 200 2 | -------------------------------------------------------------------------------- /tests/functions/main.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | ret 0; 3 | } -------------------------------------------------------------------------------- /tests/literals/i8_expected.txt: -------------------------------------------------------------------------------- 1 | -128 127 2 | -128 127 3 | -------------------------------------------------------------------------------- /tests/literals/u16_expected.txt: -------------------------------------------------------------------------------- 1 | 0 65535 2 | 0 65535 3 | -------------------------------------------------------------------------------- /tests/operators/predicate_expected.txt: -------------------------------------------------------------------------------- 1 | && 0 2 | || 1 3 | -------------------------------------------------------------------------------- /tests/structs/multiple_access_expected.txt: -------------------------------------------------------------------------------- 1 | key: 100 2 | -------------------------------------------------------------------------------- /tests/literals/string_expected.txt: -------------------------------------------------------------------------------- 1 | test 2 | 3 | 4 | xx 5 | -------------------------------------------------------------------------------- /tests/structs/global_declaration_expected.txt: -------------------------------------------------------------------------------- 1 | key: 123 321 2 | -------------------------------------------------------------------------------- /tests/structs/multiple_access_ptr_expected.txt: -------------------------------------------------------------------------------- 1 | key: 123 321 2 | -------------------------------------------------------------------------------- /tests/variables/declaration_assignment_expected.txt: -------------------------------------------------------------------------------- 1 | 100 2 | -------------------------------------------------------------------------------- /setup.bat: -------------------------------------------------------------------------------- 1 | call third_party\premake\premake5.exe vs2022 2 | PAUSE 3 | -------------------------------------------------------------------------------- /tests/literals/i16_expected.txt: -------------------------------------------------------------------------------- 1 | -32768 32767 2 | -32768 32767 3 | -------------------------------------------------------------------------------- /tests/literals/u32_expected.txt: -------------------------------------------------------------------------------- 1 | 0 4294967295 2 | 0 4294967295 3 | -------------------------------------------------------------------------------- /tests/operators/logical_not_expected.txt: -------------------------------------------------------------------------------- 1 | 1 2 | 0 3 | 1 4 | 0 5 | -------------------------------------------------------------------------------- /tests/structs/members_expected.txt: -------------------------------------------------------------------------------- 1 | x: 10 2 | y: 20 3 | arr: 1 2 4 | -------------------------------------------------------------------------------- /tests/structs/copy_expected.txt: -------------------------------------------------------------------------------- 1 | key: 123 321 111 2 | key: 123 321 111 3 | -------------------------------------------------------------------------------- /tests/variables/declaration_reassignment_expected.txt: -------------------------------------------------------------------------------- 1 | 100 2 | 200 3 | -------------------------------------------------------------------------------- /tests/operators/math_expected.txt: -------------------------------------------------------------------------------- 1 | 300 2 | -100 3 | 20000 4 | 63 5 | -50000 6 | -------------------------------------------------------------------------------- /tests/variables/declaration.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | i32 value; 3 | ret 0; 4 | } 5 | -------------------------------------------------------------------------------- /tests/literals/i32_expected.txt: -------------------------------------------------------------------------------- 1 | -2147483648 2147483647 2 | -2147483648 2147483647 3 | -------------------------------------------------------------------------------- /tests/literals/u64_expected.txt: -------------------------------------------------------------------------------- 1 | 0 18446744073709551615 2 | 0 18446744073709551615 3 | -------------------------------------------------------------------------------- /images/banner_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Goubermouche/sigma/HEAD/images/banner_1.png -------------------------------------------------------------------------------- /images/banner_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Goubermouche/sigma/HEAD/images/banner_2.png -------------------------------------------------------------------------------- /images/banner_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Goubermouche/sigma/HEAD/images/banner_3.png -------------------------------------------------------------------------------- /tests/branches/if_ret.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | if(true) { 3 | ret 0; 4 | } 5 | 6 | ret 0; 7 | } 8 | -------------------------------------------------------------------------------- /tests/casting/explicit.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | i32* memory = cast(malloc(20)); 3 | ret 0; 4 | } 5 | -------------------------------------------------------------------------------- /tests/structs/pass_by_value_expected.txt: -------------------------------------------------------------------------------- 1 | key: 123 321 111 2 | key: 123 321 222 3 | key: 123 321 111 4 | -------------------------------------------------------------------------------- /tests/branches/if.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | if(true) { 3 | printf("true\n"); 4 | } 5 | 6 | ret 0; 7 | } 8 | -------------------------------------------------------------------------------- /tests/functions/no_parameters.s: -------------------------------------------------------------------------------- 1 | i32 test() { 2 | ret 0; 3 | } 4 | 5 | i32 main() { 6 | ret 0; 7 | } -------------------------------------------------------------------------------- /third_party/premake/premake5.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Goubermouche/sigma/HEAD/third_party/premake/premake5.exe -------------------------------------------------------------------------------- /tests/functions/one_parameter.s: -------------------------------------------------------------------------------- 1 | i32 test(i32 parameter) { 2 | ret 0; 3 | } 4 | 5 | i32 main() { 6 | ret 0; 7 | } -------------------------------------------------------------------------------- /tests/literals/i64_expected.txt: -------------------------------------------------------------------------------- 1 | -9223372036854775808 9223372036854775807 2 | -9223372036854775808 9223372036854775807 3 | -------------------------------------------------------------------------------- /tests/functions/multiple_parameters.s: -------------------------------------------------------------------------------- 1 | i32 test(i32 a, i32 b) { 2 | ret 0; 3 | } 4 | 5 | i32 main() { 6 | ret 0; 7 | } -------------------------------------------------------------------------------- /tests/variables/declaration_assignment.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | i32 value = 100; 3 | printf("%d\n", value); 4 | ret 0; 5 | } 6 | -------------------------------------------------------------------------------- /tests/variables/reassignment.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | i32 value; 3 | value = 200; 4 | printf("%d\n", value); 5 | ret 0; 6 | } 7 | -------------------------------------------------------------------------------- /tests/random_keywords/alignof_expected.txt: -------------------------------------------------------------------------------- 1 | 1 2 | 2 3 | 4 4 | 8 5 | 1 6 | 2 7 | 4 8 | 8 9 | 1 10 | 1 11 | 8 12 | 8 13 | 4 14 | 8 15 | -------------------------------------------------------------------------------- /tests/branches/if_else_ret.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | if(true) { 3 | ret 0; 4 | } 5 | else { 6 | ret 0; 7 | } 8 | 9 | ret 0; 10 | } 11 | -------------------------------------------------------------------------------- /tests/pointers/declaration.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | i32* a; 3 | i32** aa; 4 | i32*** aaa; 5 | i32**** aaaa; 6 | 7 | ret 0; 8 | } 9 | -------------------------------------------------------------------------------- /tests/random_keywords/sizeof_expected.txt: -------------------------------------------------------------------------------- 1 | 1 2 | 2 3 | 4 4 | 8 5 | 1 6 | 2 7 | 4 8 | 8 9 | 1 10 | 1 11 | 8 12 | 8 13 | 4 14 | 16 15 | 8 16 | -------------------------------------------------------------------------------- /tests/branches/if_cast.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | i32 value = 10; 3 | 4 | if(value) { 5 | printf("true\n"); 6 | } 7 | 8 | ret 0; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /tests/namespaces/shallow_declaration.s: -------------------------------------------------------------------------------- 1 | namespace test { 2 | i32 test() { 3 | ret 20; 4 | } 5 | } 6 | 7 | i32 main() { 8 | ret 0; 9 | } -------------------------------------------------------------------------------- /tests/structs/variable_declaration.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | struct x { 3 | i32 value; 4 | char* x; 5 | }; 6 | 7 | x my_x; 8 | 9 | ret 0; 10 | } 11 | -------------------------------------------------------------------------------- /tests/branches/else.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | if(false) { 3 | printf("true\n"); 4 | } 5 | else { 6 | printf("false\n"); 7 | } 8 | 9 | ret 0; 10 | } 11 | -------------------------------------------------------------------------------- /tests/operators/comparison_expected.txt: -------------------------------------------------------------------------------- 1 | 100 < 100 = 0 2 | 100 > 100 = 0 3 | 100 <= 100 = 1 4 | 100 >= 100 = 1 5 | 100 == 100 = 1 6 | 100 != 100 = 0 7 | lit: 0 8 | -------------------------------------------------------------------------------- /source/intermediate_representation/codegen/memory/allocators/allocator_base.cpp: -------------------------------------------------------------------------------- 1 | #include "allocator_base.h" 2 | 3 | namespace sigma::ir::cg { 4 | } // namespace sigma::ir -------------------------------------------------------------------------------- /tests/variables/declaration_reassignment.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | i32 value = 100; 3 | printf("%d\n", value); 4 | value = 200; 5 | printf("%d\n", value); 6 | ret 0; 7 | } 8 | -------------------------------------------------------------------------------- /tests/variables/negation.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | i32 value = 10; 3 | i32 value2 = -value; 4 | 5 | printf("%d\n", value); 6 | printf("%d\n", value2); 7 | ret 0; 8 | } 9 | -------------------------------------------------------------------------------- /tests/structs/declaration.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | struct x { 3 | i32 x; 4 | }; 5 | 6 | struct y { 7 | char** x; 8 | u8 y; 9 | }; 10 | 11 | ret 0; 12 | } 13 | -------------------------------------------------------------------------------- /tests/literals/string.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | char* str1 = "test"; 3 | char* str2 = "\n\n\n"; 4 | 5 | printf("%s %s %s\n", str1, str2, "xx"); 6 | 7 | ret 0; 8 | } 9 | -------------------------------------------------------------------------------- /tests/operators/predicate.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | bool a = true; 3 | bool b = false; 4 | 5 | printf("&& %d\n", a && b); 6 | printf("|| %d\n", a || b); 7 | 8 | ret 0; 9 | } -------------------------------------------------------------------------------- /tests/namespaces/shallow_call.s: -------------------------------------------------------------------------------- 1 | namespace test { 2 | i32 test() { 3 | ret 20; 4 | } 5 | } 6 | 7 | i32 main() { 8 | printf("%d\n", test::test()); 9 | ret 0; 10 | } -------------------------------------------------------------------------------- /tests/arrays/1D.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | i32* memory = cast(malloc(100)); 3 | 4 | memory[0] = 1; 5 | memory[1] = 2; 6 | 7 | printf("%d %d\n", memory[0], memory[1]); 8 | 9 | ret 0; 10 | } -------------------------------------------------------------------------------- /tests/literals/bool.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | printf("%d %d\n", true, false); 3 | 4 | bool a = true; 5 | bool b = false; 6 | printf("%d %d\n", a, b); 7 | 8 | ret 0; 9 | } 10 | -------------------------------------------------------------------------------- /tests/literals/char.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | char c1 = 'x'; 3 | printf("%c %c\n", c1, 'y'); 4 | 5 | char c2 = 109; // m 6 | printf("%c %c\n", c2, 109); 7 | 8 | ret 0; 9 | } 10 | -------------------------------------------------------------------------------- /tests/functions/call_call.s: -------------------------------------------------------------------------------- 1 | i32 run(i32 x) { 2 | ret x; 3 | } 4 | 5 | u64 test() { 6 | ret 12; 7 | } 8 | 9 | i32 main() { 10 | printf("%d\n", run(test())); 11 | ret 0; 12 | } 13 | -------------------------------------------------------------------------------- /tests/functions/variants.s: -------------------------------------------------------------------------------- 1 | i32 test(i32 a, i32 b) { 2 | ret 0; 3 | } 4 | 5 | i32 test(i32 a) { 6 | ret 0; 7 | } 8 | 9 | i32 main() { 10 | test(1); 11 | test(1, 2); 12 | ret 0; 13 | } -------------------------------------------------------------------------------- /tests/branches/else_if.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | if(false) { 3 | printf("1\n"); 4 | } 5 | else if(false) { 6 | printf("2\n"); 7 | } 8 | else { 9 | printf("3\n"); 10 | } 11 | 12 | ret 0; 13 | } 14 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "source/utility"] 2 | path = source/utility 3 | url = https://github.com/Goubermouche/utility 4 | [submodule "source/parametric"] 5 | path = source/parametric 6 | url = https://github.com/Goubermouche/parametric 7 | -------------------------------------------------------------------------------- /tests/operators/logical_not.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | bool a = true; 3 | printf("%d\n", a); 4 | printf("%d\n", !a); 5 | 6 | bool b = 100; 7 | printf("%d\n", b); 8 | printf("%d\n", !b); 9 | 10 | ret 0; 11 | } 12 | -------------------------------------------------------------------------------- /tests/literals/u8.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | u8 min = 0; 3 | u8 max = 255; 4 | 5 | printf("%u %u\n", min, max); 6 | 7 | u8 overflow = 256; 8 | u8 underflow = -1; 9 | 10 | printf("%u %u\n", overflow, underflow); 11 | 12 | ret 0; 13 | } 14 | -------------------------------------------------------------------------------- /tests/literals/i8.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | i8 min = -128; 3 | i8 max = 127; 4 | 5 | printf("%d %d\n", min, max); 6 | 7 | i8 overflow = 128; 8 | i8 underflow = -129; 9 | 10 | printf("%d %d\n", overflow, underflow); 11 | 12 | ret 0; 13 | } 14 | -------------------------------------------------------------------------------- /tests/literals/u16.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | u16 min = 0; 3 | u16 max = 65535; 4 | 5 | printf("%u %u\n", min, max); 6 | 7 | u16 overflow = 65536; 8 | u16 underflow = -1; 9 | 10 | printf("%u %u\n", overflow, underflow); 11 | 12 | ret 0; 13 | } 14 | -------------------------------------------------------------------------------- /tests/literals/i16.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | i16 min = -32768; 3 | i16 max = 32767; 4 | 5 | printf("%d %d\n", min, max); 6 | 7 | i16 overflow = -32769; 8 | i16 underflow = 32768; 9 | 10 | printf("%d %d\n", overflow, underflow); 11 | 12 | ret 0; 13 | } 14 | -------------------------------------------------------------------------------- /tests/literals/u32.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | u32 min = 0; 3 | u32 max = 4294967295; 4 | 5 | printf("%u %u\n", min, max); 6 | 7 | u32 overflow = 4294967296; 8 | u32 underflow = -1; 9 | 10 | printf("%u %u\n", overflow, underflow); 11 | 12 | ret 0; 13 | } 14 | -------------------------------------------------------------------------------- /source/intermediate_representation/codegen/transformation/scheduler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "intermediate_representation/codegen/codegen_context.h" 3 | 4 | namespace sigma::ir { 5 | void schedule_node_hierarchy(codegen_context& context); 6 | } // namespace sigma::ir 7 | -------------------------------------------------------------------------------- /tests/structs/multiple_access.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | struct key { 3 | i32 value; 4 | }; 5 | 6 | struct user { 7 | key k; 8 | }; 9 | 10 | user my_user; 11 | 12 | my_user.k.value = 100; 13 | 14 | printf("key: %d\n", my_user.k.value); 15 | ret 0; 16 | } 17 | -------------------------------------------------------------------------------- /source/intermediate_representation/codegen/transformation/live_range_analysis.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "intermediate_representation/codegen/codegen_context.h" 3 | 4 | namespace sigma::ir { 5 | void determine_live_ranges(codegen_context& context); 6 | } // namespace sigma::ir 7 | -------------------------------------------------------------------------------- /tests/literals/i32.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | i32 min = -2147483648; 3 | i32 max = 2147483647; 4 | 5 | printf("%d %d\n", min, max); 6 | 7 | i32 overflow = -2147483649; 8 | i32 underflow = 2147483648; 9 | 10 | printf("%d %d\n", overflow, underflow); 11 | 12 | ret 0; 13 | } 14 | -------------------------------------------------------------------------------- /tests/literals/u64.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | u64 min = 0; 3 | u64 max = 18446744073709551615; 4 | 5 | printf("%llu %llu\n", min, max); 6 | 7 | u64 overflow = 18446744073709551616; 8 | u64 underflow = -1; 9 | 10 | printf("%llu %llu\n", overflow, underflow); 11 | 12 | ret 0; 13 | } 14 | -------------------------------------------------------------------------------- /tests/literals/i64.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | i64 min = -9223372036854775808; 3 | i64 max = 9223372036854775807; 4 | 5 | printf("%lld %lld\n", min, max); 6 | 7 | i64 overflow = -9223372036854775808; 8 | i64 underflow = 9223372036854775807; 9 | 10 | printf("%lld %lld\n", overflow, underflow); 11 | 12 | ret 0; 13 | } 14 | -------------------------------------------------------------------------------- /source/intermediate_representation/codegen/memory/allocators/allocator_base.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace sigma::ir { 4 | struct codegen_context; 5 | 6 | class allocator_base { 7 | public: 8 | virtual void allocate(codegen_context& context) = 0; 9 | virtual ~allocator_base() = default; 10 | }; 11 | } // namespace sigma::ir 12 | -------------------------------------------------------------------------------- /tests/namespaces/deep_declaration.s: -------------------------------------------------------------------------------- 1 | namespace a { 2 | namespace a { 3 | namespace a { 4 | namespace a { 5 | namespace a { 6 | i32 test() { 7 | ret 20; 8 | } 9 | } 10 | } 11 | } 12 | } 13 | } 14 | 15 | i32 main() { 16 | ret 0; 17 | } -------------------------------------------------------------------------------- /tests/structs/multiple_access_ptr.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | struct key { 3 | i32* value; 4 | }; 5 | 6 | struct user { 7 | key k; 8 | }; 9 | 10 | user my_user; 11 | 12 | my_user.k.value = cast(malloc(sizeof(i32) * 2)); 13 | my_user.k.value[0] = 123; 14 | my_user.k.value[1] = 321; 15 | 16 | printf("key: %d %d\n", my_user.k.value[0], my_user.k.value[1]); 17 | ret 0; 18 | } 19 | -------------------------------------------------------------------------------- /tests/structs/global_declaration.s: -------------------------------------------------------------------------------- 1 | struct key { 2 | i32* value; 3 | }; 4 | 5 | i32 main() { 6 | struct user { 7 | key k; 8 | }; 9 | 10 | user my_user; 11 | 12 | my_user.k.value = cast(malloc(sizeof(i32) * 2)); 13 | my_user.k.value[0] = 123; 14 | my_user.k.value[1] = 321; 15 | 16 | printf("key: %d %d\n", my_user.k.value[0], my_user.k.value[1]); 17 | 18 | ret 0; 19 | } 20 | -------------------------------------------------------------------------------- /source/intermediate_representation/node_hierarchy/user.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "intermediate_representation/node_hierarchy/node.h" 3 | 4 | namespace sigma::ir { 5 | /** 6 | * \brief Describes an entity which uses a given \a node at some point in 7 | * it's lifetime 8 | */ 9 | struct user { 10 | handle next_user; 11 | handle target; 12 | u64 slot; 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /tests/structs/copy.s: -------------------------------------------------------------------------------- 1 | struct user { 2 | i32* k; 3 | i32 x; 4 | }; 5 | 6 | i32 main() { 7 | user usr1; 8 | 9 | usr1.k = cast(malloc(8)); 10 | usr1.k[0] = 123; 11 | usr1.k[1] = 321; 12 | usr1.x = 111; 13 | 14 | user usr2 = usr1; 15 | 16 | printf("key: %d %d %d\n", usr1.k[0], usr1.k[1], usr1.x); 17 | printf("key: %d %d %d\n", usr2.k[0], usr2.k[1], usr2.x); 18 | 19 | ret 0; 20 | } 21 | -------------------------------------------------------------------------------- /source/intermediate_representation/module_output.cpp: -------------------------------------------------------------------------------- 1 | #include "module_output.h" 2 | 3 | namespace sigma::ir { 4 | void module_output::add_section(const std::string& name, module_section::module_section_flags flags, comdat::comdat_type comdat) { 5 | sections.push_back({ 6 | .name = std::string(name), 7 | .flags = flags, 8 | .com = {.ty = comdat } 9 | }); 10 | } 11 | } // namespace sigma::ir 12 | -------------------------------------------------------------------------------- /source/intermediate_representation/target/parameter_descriptor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "intermediate_representation/codegen/memory/memory.h" 3 | 4 | namespace sigma::ir { 5 | using namespace utility::types; 6 | 7 | struct parameter_descriptor { 8 | u16 gpr_count; 9 | u16 xmm_count; 10 | u16 caller_saved_xmm_count; 11 | u16 caller_saved_gpr_count; 12 | reg gpr_registers[6]; 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /tests/arrays/3D.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | i32*** memory = cast(malloc(100)); 3 | 4 | memory[0] = cast(malloc(100)); 5 | memory[1] = cast(malloc(100)); 6 | 7 | memory[0][0] = cast(malloc(100)); 8 | memory[1][0] = cast(malloc(100)); 9 | 10 | memory[0][0][0] = 1; 11 | memory[1][0][0] = 2; 12 | 13 | printf("%d %d\n", memory[0][0][0], memory[1][0][0]); 14 | ret 0; 15 | } 16 | -------------------------------------------------------------------------------- /tests/namespaces/deep_call.s: -------------------------------------------------------------------------------- 1 | namespace a { 2 | namespace a { 3 | namespace a { 4 | namespace a { 5 | namespace a { 6 | i32 test() { 7 | ret 20; 8 | } 9 | } 10 | } 11 | } 12 | } 13 | } 14 | 15 | i32 main() { 16 | printf("%d\n", a::a::a::a::a::test()); 17 | ret 0; 18 | } -------------------------------------------------------------------------------- /tests/operators/math.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | i32 value1 = 100 + 200; 3 | i32 value2 = 100 - 200; 4 | i32 value3 = 100 * 200; 5 | u8 value4 = 32 * 10 - 1; // 63 6 | 7 | i32 precedence = value1 * value2 - value3; 8 | 9 | printf("%d\n", value1); 10 | printf("%d\n", value2); 11 | printf("%d\n", value3); 12 | printf("%d\n", value4); 13 | printf("%d\n", precedence); 14 | ret 0; 15 | } 16 | -------------------------------------------------------------------------------- /tests/operators/comparison.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | i32 a = 100; 3 | i32 b = 100; 4 | 5 | printf("%d < %d = %d\n", a, b, a < b); 6 | printf("%d > %d = %d\n", a, b, a > b); 7 | printf("%d <= %d = %d\n", a, b, a <= b); 8 | printf("%d >= %d = %d\n", a, b, a >= b); 9 | printf("%d == %d = %d\n", a, b, a == b); 10 | printf("%d != %d = %d\n", a, b, a != b); 11 | 12 | printf("lit: %d\n", 100 > 200); 13 | 14 | ret 0; 15 | } 16 | -------------------------------------------------------------------------------- /source/intermediate_representation/codegen/transformation/use_list.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "intermediate_representation/codegen/transformation/transformation_context.h" 3 | 4 | namespace sigma::ir { 5 | /** 6 | * \brief Generates usage lists for all nodes in the given \a context 7 | * \param context Code generation context 8 | */ 9 | void generate_use_lists(transformation_context& context); 10 | } // namespace sigma::ir 11 | -------------------------------------------------------------------------------- /source/intermediate_representation/codegen/transformation/transformation_context.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "intermediate_representation/node_hierarchy/function.h" 3 | #include "intermediate_representation/codegen/work_list.h" 4 | 5 | namespace sigma::ir { 6 | struct transformation_context { 7 | handle function; 8 | work_list& work; 9 | 10 | // locals 11 | std::vector> locals; 12 | }; 13 | } // namespace sigma::ir 14 | -------------------------------------------------------------------------------- /source/intermediate_representation/node_hierarchy/properties/memory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace sigma::ir { 5 | using namespace utility::types; 6 | 7 | struct local { 8 | u32 size; // in bytes 9 | u32 alignment; // in bytes 10 | }; 11 | 12 | struct memory_access { 13 | u32 alignment; // in bytes 14 | }; 15 | 16 | struct array { 17 | i64 stride; 18 | }; 19 | 20 | struct member { 21 | i64 offset; 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /source/intermediate_representation/node_hierarchy/properties/types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace sigma::ir { 5 | using namespace utility::types; 6 | 7 | enum class float_format { 8 | F32, 9 | F64 10 | }; 11 | 12 | struct integer { 13 | u64 value = 0; 14 | u8 bit_width = 0; 15 | }; 16 | 17 | struct floating_point_32 { 18 | f32 value = 0.0f; 19 | }; 20 | 21 | struct floating_point_64 { 22 | f64 value = 0.0; 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /tests/structs/namespaces.s: -------------------------------------------------------------------------------- 1 | namespace x { 2 | struct user { 3 | i32* k; 4 | i32 x; 5 | }; 6 | } 7 | 8 | namespace y { 9 | struct user { 10 | i32* k; 11 | i32 x; 12 | }; 13 | } 14 | 15 | void print(x::user usr) { 16 | printf("%d\n", usr.x); 17 | } 18 | 19 | i32 main() { 20 | x::user my_user; 21 | 22 | my_user.k = cast(malloc(8)); 23 | my_user.k[0] = 123; 24 | my_user.k[1] = 321; 25 | my_user.x = 111; 26 | 27 | print(my_user); 28 | 29 | ret 0; 30 | } 31 | -------------------------------------------------------------------------------- /source/intermediate_representation/codegen/memory/memory.cpp: -------------------------------------------------------------------------------- 1 | #include "memory.h" 2 | 3 | namespace sigma::ir { 4 | reg::reg(id_type id) : id(id) {} 5 | 6 | auto reg::operator==(reg other) const -> bool { 7 | return id == other.id; 8 | } 9 | 10 | auto reg::is_valid() const -> bool { 11 | return id != invalid_id; 12 | } 13 | 14 | auto classified_reg::operator==(classified_reg other) const -> bool { 15 | return id == other.id && cl == other.cl; 16 | } 17 | } // namespace sigma::ir 18 | -------------------------------------------------------------------------------- /source/intermediate_representation/codegen/optimization/optimization_pass_list.cpp: -------------------------------------------------------------------------------- 1 | #include "optimization_pass_list.h" 2 | 3 | namespace sigma::ir { 4 | optimization_pass_list::optimization_pass_list( 5 | const std::vector>& passes 6 | ) : m_passes(passes) {} 7 | 8 | void optimization_pass_list::apply(transformation_context& context) const { 9 | for (const s_ptr& pass : m_passes) { 10 | pass->apply(context); 11 | } 12 | } 13 | } // namespace sigma::ir 14 | -------------------------------------------------------------------------------- /tests/structs/pass_by_value.s: -------------------------------------------------------------------------------- 1 | struct user { 2 | i32* k; 3 | i32 x; 4 | }; 5 | 6 | void print(user usr) { 7 | usr.x = 222; 8 | printf("key: %d %d %d\n", usr.k[0], usr.k[1], usr.x); 9 | } 10 | 11 | i32 main() { 12 | user my_user; 13 | 14 | my_user.k = cast(malloc(8)); 15 | my_user.k[0] = 123; 16 | my_user.k[1] = 321; 17 | my_user.x = 111; 18 | 19 | printf("key: %d %d %d\n", my_user.k[0], my_user.k[1], my_user.x); 20 | print(my_user); 21 | printf("key: %d %d %d\n", my_user.k[0], my_user.k[1], my_user.x); 22 | 23 | ret 0; 24 | } 25 | -------------------------------------------------------------------------------- /tests/structs/members.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | struct vec2 { 3 | i32 x; 4 | i32 y; 5 | i32** arr; 6 | }; 7 | 8 | vec2 instance; 9 | 10 | instance.x = 10; 11 | instance.y = 20; 12 | 13 | instance.arr = cast(malloc(16)); 14 | instance.arr[0] = cast(malloc(4)); 15 | instance.arr[1] = cast(malloc(4)); 16 | instance.arr[0][0] = 1; 17 | instance.arr[1][0] = 2; 18 | 19 | printf("x: %d\n", instance.x); 20 | printf("y: %d\n", instance.y); 21 | printf("arr: %d %d\n", instance.arr[0][0], instance.arr[1][0]); 22 | 23 | ret 0; 24 | } 25 | -------------------------------------------------------------------------------- /source/intermediate_representation/codegen/work_list.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "intermediate_representation/codegen/control_flow_graph.h" 3 | 4 | namespace sigma::ir { 5 | struct work_list { 6 | auto mark_next_control(handle target) -> handle; 7 | auto visit(handle node) -> bool; 8 | 9 | void compute_dominators(control_flow_graph& cfg) const; 10 | void push_all(handle function); 11 | void clear(); 12 | 13 | std::unordered_set> visited_items; 14 | std::vector> items; 15 | }; 16 | }// namespace sigma::ir 17 | -------------------------------------------------------------------------------- /source/intermediate_representation/node_hierarchy/symbol.cpp: -------------------------------------------------------------------------------- 1 | #include "symbol.h" 2 | #include "intermediate_representation/node_hierarchy/function.h" 3 | 4 | namespace sigma::ir { 5 | symbol::symbol(symbol_type type, const std::string& name, handle parent_module, linkage linkage) 6 | : type(type), name(name), parent_module(parent_module), link(linkage) {} 7 | 8 | auto symbol::is_non_local() const -> bool { 9 | if (type == GLOBAL || type == FUNCTION) { 10 | return link == linkage::PUBLIC; 11 | } 12 | 13 | return true; 14 | } 15 | } // namespace sigma::ir 16 | -------------------------------------------------------------------------------- /source/compiler/test/main.s: -------------------------------------------------------------------------------- 1 | // INFO: 2 | // - linking: link /OUT:a.exe a.obj /LIBPATH:C:\dev\projects\sigma\source\compiler\test\libraries libcmt.lib libvcruntime.lib libucrt.lib 3 | 4 | // KNOWN BUGS 5 | // - numerical literals throw cast warnings, which shouldnt happen 6 | // - numerical literals are always interpreted as i32 7 | 8 | // TODO: 9 | // - DIAGNOSTICS: 10 | // - better messages 11 | // - more info related to numerical errors (hex etc) 12 | // - add namespaces to messages, whenever applicable (ie. x::y::test) 13 | // - BUGS: 14 | 15 | u64 main() { 16 | printf("abcd\n"); 17 | ret 0; 18 | } 19 | -------------------------------------------------------------------------------- /source/intermediate_representation/node_hierarchy/properties/operations.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include "intermediate_representation/node_hierarchy/types.h" 5 | 6 | namespace sigma::ir { 7 | using namespace utility::types; 8 | 9 | enum class arithmetic_behaviour { 10 | NONE = 0, 11 | NO_SIGNED_WRAP = 1, 12 | NO_UNSIGNED_WRAP = 2 13 | }; 14 | 15 | // declare as a flag enum 16 | FLAG_ENUM(arithmetic_behaviour); 17 | 18 | struct binary_integer_op { 19 | arithmetic_behaviour behaviour = arithmetic_behaviour::NONE; 20 | }; 21 | 22 | struct compare_op { 23 | data_type cmp_dt; 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /source/intermediate_representation/node_hierarchy/properties/control_flow.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "intermediate_representation/node_hierarchy/types.h" 3 | 4 | namespace sigma::ir { 5 | using namespace utility::types; 6 | 7 | struct node; 8 | struct function; 9 | 10 | struct region { 11 | handle memory_in; 12 | handle memory_out; 13 | }; 14 | 15 | struct branch { 16 | std::vector> successors; 17 | std::vector keys; 18 | }; 19 | 20 | struct projection { 21 | u64 index = 0; 22 | }; 23 | 24 | struct function_call { 25 | function_signature signature; // callee 26 | std::vector> projections; 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /tests/random_keywords/alignof.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | printf("%d\n", alignof(i8)); 3 | printf("%d\n", alignof(i16)); 4 | printf("%d\n", alignof(i32)); 5 | printf("%d\n", alignof(i64)); 6 | printf("%d\n", alignof(u8)); 7 | printf("%d\n", alignof(u16)); 8 | printf("%d\n", alignof(u32)); 9 | printf("%d\n", alignof(u64)); 10 | printf("%d\n", alignof(bool)); 11 | printf("%d\n", alignof(char)); 12 | printf("%d\n", alignof(i32*)); 13 | printf("%d\n", alignof(i32**)); 14 | 15 | struct s1 { 16 | i32 a; 17 | }; 18 | 19 | printf("%d\n", alignof(s1)); 20 | 21 | struct s2 { 22 | i32 a; 23 | char** b; 24 | }; 25 | 26 | printf("%d\n", alignof(s2)); 27 | 28 | ret 0; 29 | } 30 | -------------------------------------------------------------------------------- /tests/random_keywords/sizeof.s: -------------------------------------------------------------------------------- 1 | i32 main() { 2 | printf("%d\n", sizeof(i8)); 3 | printf("%d\n", sizeof(i16)); 4 | printf("%d\n", sizeof(i32)); 5 | printf("%d\n", sizeof(i64)); 6 | printf("%d\n", sizeof(u8)); 7 | printf("%d\n", sizeof(u16)); 8 | printf("%d\n", sizeof(u32)); 9 | printf("%d\n", sizeof(u64)); 10 | printf("%d\n", sizeof(bool)); 11 | printf("%d\n", sizeof(char)); 12 | printf("%d\n", sizeof(i32*)); 13 | printf("%d\n", sizeof(i32**)); 14 | 15 | struct s1 { 16 | i32 a; 17 | }; 18 | 19 | printf("%d\n", sizeof(s1)); 20 | 21 | struct s2 { 22 | i32 a; 23 | char** b; 24 | }; 25 | 26 | printf("%d\n", sizeof(s2)); 27 | printf("%d\n", sizeof(s2*)); 28 | 29 | ret 0; 30 | } 31 | -------------------------------------------------------------------------------- /source/intermediate_representation/codegen/transformation/use_list.cpp: -------------------------------------------------------------------------------- 1 | #include "use_list.h" 2 | 3 | namespace sigma::ir { 4 | void generate_use_lists(transformation_context& context) { 5 | context.work.push_all(context.function); 6 | 7 | for (const handle item : context.work.items) { 8 | // append locals 9 | if (item == node::type::LOCAL) { 10 | context.locals.emplace_back(item); 11 | } 12 | 13 | // mark every node as a user of all of its input nodes 14 | for (u64 i = 0; i < item->inputs.get_size(); ++i) { 15 | if (const handle input = item->inputs[i]) { 16 | item->add_user(input, i, nullptr, &context.function->allocator); 17 | } 18 | } 19 | } 20 | 21 | context.work.clear(); 22 | } 23 | } // namespace sigma::ir 24 | -------------------------------------------------------------------------------- /source/intermediate_representation/codegen/optimization/optimization_pass_list.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "intermediate_representation/codegen/transformation/transformation_context.h" 3 | 4 | namespace sigma::ir { 5 | class optimization_pass { 6 | public: 7 | virtual void apply(transformation_context& context) = 0; 8 | virtual ~optimization_pass() = default; 9 | }; 10 | 11 | // TODO: we'll probably need to add a priority to each pass and sort 12 | // them before applying them 13 | 14 | class optimization_pass_list { 15 | public: 16 | optimization_pass_list(const std::vector>& passes); 17 | 18 | void apply(transformation_context& context) const; 19 | private: 20 | std::vector> m_passes; 21 | }; 22 | } // namespace sigma::ir 23 | -------------------------------------------------------------------------------- /.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | name: Windows 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | runs-on: windows-2022 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | with: 16 | fetch-depth: 0 17 | submodules: true 18 | 19 | - name: Setup MSBuild 20 | uses: microsoft/setup-msbuild@v1.1 21 | 22 | - name: Run Premake 23 | run: .\setup.bat 24 | 25 | - name: Compile Project (Compiler) 26 | run: msbuild sigma.sln /p:Configuration=Debug /p:Platform="x64" 27 | 28 | - name: Compile Project (Tests) 29 | run: msbuild tests.sln /p:Configuration=Debug /p:Platform="x64" 30 | 31 | - name: Run Tests 32 | run: output\tests\bin\Debug\tests.exe run tests output\compiler\bin\Debug\compiler.exe 33 | -------------------------------------------------------------------------------- /source/intermediate_representation/target/arch/architecture.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #include "intermediate_representation/codegen/codegen_context.h" 6 | 7 | namespace sigma::ir { 8 | class disassembler { 9 | public: 10 | virtual auto disassemble(const utility::byte_buffer& bytecode, const codegen_context& context) -> std::stringstream = 0; 11 | 12 | virtual ~disassembler() = default; 13 | }; 14 | 15 | class architecture { 16 | public: 17 | virtual auto emit_bytecode(codegen_context& context) -> utility::byte_buffer = 0; 18 | virtual auto get_register_intervals() -> std::vector = 0; 19 | virtual void select_instructions(codegen_context& context) = 0; 20 | 21 | virtual ~architecture() = default; 22 | }; 23 | } // namespace sigma::ir 24 | -------------------------------------------------------------------------------- /source/intermediate_representation/codegen/control_flow_graph.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "intermediate_representation/codegen/live_interval.h" 3 | 4 | namespace sigma::ir { 5 | struct codegen_context; 6 | 7 | struct control_flow_graph { 8 | auto get_immediate_dominator(handle target) -> handle; 9 | auto get_predecessor(handle target, u64 index) -> handle; 10 | 11 | auto try_get_traversal_index(handle target) -> u64; 12 | auto get_traversal_index(handle target) const ->u64; 13 | auto resolve_dominator_depth(handle basic_block) -> i32; 14 | auto get_dominator_depth(handle target) const -> i32; 15 | 16 | static auto compute_reverse_post_order(const codegen_context& context) -> control_flow_graph; 17 | 18 | std::unordered_map, basic_block> blocks; 19 | }; 20 | } // namespace sigma::ir 21 | -------------------------------------------------------------------------------- /documents/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Welcome 4 | Heya! welcome to the Sigma project, feel free to look around or help out here and there! If you feel lost you can check out the project documentation [WIP] or visit the project discord [WIP]. 5 | 6 | ## Ways to contribute 7 | 8 | ### Contributing to the compiler implementation 9 | Before contributing to the compiler implementation please read our [**style guide**](STYLE.md) first. Pretty much anything can be a contribution, from a tiny typo fix, to an entire feature implementation - anything is possible. 10 | Another way of contributing to the compiler implementation is editing and giving suggestions related to the [**compiler design**](COMPILER_DESIGN.md) writeup. 11 | 12 | ### Contributing to the language design 13 | Before contributing to the language design implementation please read our [**language design**](LANGUAGE_DESIGN.md) writeup. 14 | -------------------------------------------------------------------------------- /source/intermediate_representation/target/target.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "intermediate_representation/target/parameter_descriptor.h" 3 | 4 | namespace sigma::ir { 5 | enum class arch : u8 { 6 | X64 7 | }; 8 | 9 | enum class system : u8 { 10 | WINDOWS, 11 | LINUX 12 | }; 13 | 14 | enum class abi : u8 { 15 | WIN_64, 16 | SYSTEMV 17 | }; 18 | 19 | /** 20 | * \brief Represents the target execution platform (architecture x system x ABI). 21 | */ 22 | class target { 23 | public: 24 | target() = default; 25 | target(arch arch, system system); 26 | 27 | [[nodiscard]] auto get_abi() const -> abi; 28 | [[nodiscard]] auto get_arch() const -> arch; 29 | [[nodiscard]] auto get_system() const -> system; 30 | 31 | [[nodiscard]] auto get_parameter_descriptor() const -> parameter_descriptor; 32 | private: 33 | abi m_abi; 34 | arch m_arch; 35 | system m_system; 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /source/intermediate_representation/node_hierarchy/global.cpp: -------------------------------------------------------------------------------- 1 | #include "global.h" 2 | #include "intermediate_representation/module.h" 3 | 4 | namespace sigma::ir { 5 | global::global(ir::symbol sym) : symbol(sym) {} 6 | 7 | void global::set_storage( 8 | u8 section_handle, u32 storage_size, u32 storage_alignment, u32 max_objects 9 | ) { 10 | position = 0; 11 | size = storage_size; 12 | alignment = storage_alignment; 13 | parent_section = section_handle; 14 | objects.reserve(max_objects); 15 | } 16 | 17 | auto global::add_region(u32 region_offset, u32 region_size) -> void* { 18 | void* ptr = utility::malloc(region_size); 19 | 20 | const init_object object { 21 | .type = init_object::REGION, 22 | .offset = region_offset, 23 | .region = { 24 | .size = region_size, 25 | .ptr = ptr 26 | } 27 | }; 28 | 29 | objects.push_back(object); 30 | return ptr; 31 | } 32 | } // namespace sigma::ir 33 | -------------------------------------------------------------------------------- /source/intermediate_representation/codegen/memory/memory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace sigma::ir { 5 | using namespace utility::types; 6 | 7 | struct reg { 8 | using id_type = u8; 9 | 10 | reg() = default; 11 | reg(id_type id); 12 | 13 | auto operator==(reg other) const -> bool; 14 | auto is_valid() const -> bool; 15 | 16 | static constexpr id_type invalid_id = std::numeric_limits::max(); 17 | id_type id = invalid_id; 18 | }; 19 | 20 | struct classified_reg : reg { 21 | using class_type = u8; 22 | 23 | auto operator==(classified_reg other) const -> bool; 24 | 25 | static constexpr class_type invalid_class = std::numeric_limits::max(); 26 | class_type cl = invalid_class; 27 | }; 28 | 29 | enum class memory_scale : u8 { 30 | x1, x2, x4, x8 31 | }; 32 | 33 | struct mem { 34 | u8 index; 35 | memory_scale scale; 36 | i32 displacement; 37 | }; 38 | } // namespace sigma::ir 39 | -------------------------------------------------------------------------------- /source/ir_translator/values/function_registry.cpp: -------------------------------------------------------------------------------- 1 | #include "function_registry.h" 2 | 3 | namespace sigma::detail { 4 | function_registry::function_registry(ir::builder& builder) : m_builder(builder) {} 5 | 6 | auto function_registry::create_call( 7 | utility::string_table_key identifier_key, const std::vector>& parameters 8 | ) -> handle { 9 | // attempt to call a local function 10 | const auto func_it = m_functions.find(identifier_key); 11 | if (func_it != m_functions.end()) { 12 | return m_builder.create_call(func_it->second, parameters); 13 | } 14 | 15 | // attempt to call an external function 16 | const auto external_func_it = m_external_functions.find(identifier_key); 17 | if (external_func_it != m_external_functions.end()) { 18 | return m_builder.create_call( 19 | external_func_it->second.external, external_func_it->second.signature, parameters 20 | ); 21 | } 22 | 23 | return nullptr; 24 | } 25 | } // sigma::detail 26 | -------------------------------------------------------------------------------- /source/intermediate_representation/node_hierarchy/symbol.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace sigma::ir { 6 | using namespace utility::types; 7 | 8 | enum class linkage : u8 { 9 | PUBLIC, 10 | PRIVATE, 11 | SO_LOCAL, // exports to the rest of the shared object 12 | SO_EXPORT // exports outside of the shared object 13 | }; 14 | 15 | class module; 16 | struct symbol { 17 | enum symbol_type { 18 | NONE, 19 | 20 | // symbol is dead 21 | TOMBSTONE, 22 | 23 | EXTERNAL, 24 | GLOBAL, 25 | FUNCTION, 26 | 27 | MAX 28 | }; 29 | 30 | symbol() = default; 31 | symbol(symbol_type type, const std::string& name, handle parent_module, linkage linkage); 32 | auto is_non_local() const -> bool; 33 | 34 | symbol_type type; 35 | std::string name; 36 | 37 | handle parent_module; // not used rn 38 | 39 | linkage link; 40 | 41 | u64 ordinal = 0; 42 | u64 id = 0; 43 | }; 44 | } 45 | -------------------------------------------------------------------------------- /source/intermediate_representation/target/target.cpp: -------------------------------------------------------------------------------- 1 | #include "target.h" 2 | #include "intermediate_representation/target/system/systemv/systemv.h" 3 | #include "intermediate_representation/target/system/win/win.h" 4 | 5 | namespace sigma::ir { 6 | target::target(arch arch, system system) : m_arch(arch), m_system(system) { 7 | m_abi = system == system::WINDOWS ? abi::WIN_64 : abi::SYSTEMV; 8 | } 9 | 10 | auto target::get_abi() const -> abi { 11 | return m_abi; 12 | } 13 | 14 | auto target::get_arch() const -> arch { 15 | return m_arch; 16 | } 17 | 18 | auto target::get_system() const -> system { 19 | return m_system; 20 | } 21 | 22 | auto target::get_parameter_descriptor() const -> parameter_descriptor { 23 | switch(m_abi) { 24 | case abi::SYSTEMV: return systemv::parameter_descriptor; 25 | case abi::WIN_64: return win::parameter_descriptor; 26 | default: PANIC("unimplemented ABI referenced ('{}')", static_cast(m_abi)); 27 | } 28 | 29 | return {}; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /source/intermediate_representation/codegen/instruction.cpp: -------------------------------------------------------------------------------- 1 | #include "instruction.h" 2 | #include "intermediate_representation/target/arch/x64/x64.h" 3 | 4 | namespace sigma::ir { 5 | auto instruction::is_terminator() const -> bool { 6 | return m_type == type::TERMINATOR || m_type == type::INT3 || m_type == type::UD2; 7 | } 8 | 9 | void instruction::set_type(type type) { 10 | m_type = type; 11 | } 12 | 13 | auto instruction::get_type() const -> instruction::type { 14 | return m_type; 15 | } 16 | 17 | instruction::type::type() : m_type(ZERO) {} 18 | 19 | instruction::type::type(underlying type) : m_type(type) {} 20 | 21 | instruction::type::operator underlying() const { 22 | return m_type; 23 | } 24 | 25 | auto instruction::type::to_string() const -> std::string { 26 | NOT_IMPLEMENTED(); 27 | return ""; 28 | } 29 | 30 | bool operator==(handle inst, instruction::type::underlying type) { 31 | return inst->get_type() == type; 32 | } 33 | } // namespace sigma::ir 34 | -------------------------------------------------------------------------------- /source/intermediate_representation/target/outputs/object_file_emitter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace sigma::ir { 6 | using namespace utility::types; 7 | 8 | struct module_section; 9 | struct compiled_function; 10 | class module; 11 | 12 | /** 13 | * \brief Generic object file emitter, this base class should be specialized for individual 14 | * targets (ELF, COFF, etc.) 15 | */ 16 | class object_file_emitter { 17 | public: 18 | virtual utility::byte_buffer emit(module& module) = 0; 19 | virtual ~object_file_emitter() = default; 20 | protected: 21 | static auto layout_relocations(std::vector& sections, u32 output_size, u32 relocation_size) -> u32; 22 | static auto helper_write_section(u64 write_pos, const module_section* section, u32 pos, utility::byte_buffer& buffer) -> u64; 23 | static auto emit_call_patches(handle compiled_func) -> u32; 24 | }; 25 | } // namespace sigma::ir 26 | -------------------------------------------------------------------------------- /source/intermediate_representation/node_hierarchy/global.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "intermediate_representation/node_hierarchy/symbol.h" 3 | #include "intermediate_representation/node_hierarchy/types.h" 4 | 5 | namespace sigma::ir { 6 | struct init_object { 7 | enum init_object_type { 8 | REGION, 9 | RELOCATION 10 | }; 11 | 12 | handle relocation; 13 | init_object_type type; 14 | u32 offset; 15 | 16 | struct { 17 | u32 size; 18 | const void* ptr; 19 | } region; 20 | }; 21 | 22 | class module; 23 | struct module_section; 24 | 25 | struct global { 26 | global(symbol sym); 27 | 28 | void set_storage(u8 section_handle, u32 storage_size, u32 storage_alignment, u32 max_objects); 29 | auto add_region(u32 region_offset, u32 region_size) -> void*; 30 | 31 | symbol symbol; 32 | u8 parent_section; 33 | 34 | u32 position; 35 | u32 size; 36 | u32 alignment; 37 | 38 | // contents 39 | std::vector objects; 40 | }; 41 | 42 | struct external { 43 | symbol symbol; 44 | }; 45 | } // namespace sigma::ir 46 | -------------------------------------------------------------------------------- /source/compiler/compiler/type_system/namespace_list.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace sigma { 6 | using namespace utility::types; 7 | 8 | struct namespace_list { 9 | using base = utility::memory_view; 10 | 11 | namespace_list() = default; 12 | namespace_list(const utility::memory_view& namespaces); 13 | 14 | auto empty() const -> bool; 15 | auto size() const->u64; 16 | 17 | auto first() const->utility::string_table_key; 18 | auto last() const->utility::string_table_key; 19 | 20 | auto begin() -> base::iterator; 21 | auto end() -> base::iterator; 22 | 23 | auto begin() const -> base::const_iterator; 24 | auto end() const -> base::const_iterator; 25 | 26 | auto operator[](u64 index) const -> utility::string_table_key; 27 | auto operator==(const namespace_list& other) const -> bool; 28 | 29 | auto get_stream(const utility::string_table& strings) const->std::stringstream; 30 | 31 | base namespaces; 32 | }; 33 | } // namespace sigma 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023-2024 goubermouche simontupy64@gmail.com 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /source/intermediate_representation/module_output.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "intermediate_representation/node_hierarchy/function.h" 3 | 4 | namespace sigma::ir { 5 | struct comdat { 6 | enum comdat_type { 7 | NONE, 8 | MATCH_ANY 9 | }; 10 | 11 | u32 relocation_count; 12 | comdat_type ty; 13 | }; 14 | 15 | struct module_section { 16 | enum module_section_flags { 17 | NONE = 0, 18 | WRITE = 1, 19 | EXEC = 2, 20 | TLS = 4, 21 | }; 22 | 23 | std::string name; 24 | 25 | u32 export_flags; 26 | u32 name_position; 27 | module_section_flags flags; 28 | 29 | u16 section_index; 30 | comdat com; 31 | 32 | u32 raw_data_pos; 33 | u32 total_size; 34 | u32 relocation_count; 35 | u32 relocation_position; 36 | 37 | std::vector> globals; 38 | std::vector> functions; 39 | }; 40 | 41 | FLAG_ENUM(module_section::module_section_flags); 42 | 43 | struct module_output { 44 | void add_section(const std::string& name, module_section::module_section_flags flags, comdat::comdat_type comdat); 45 | 46 | std::vector sections; 47 | handle chkstk_extern = nullptr; 48 | }; 49 | } // namespace sigma::ir 50 | -------------------------------------------------------------------------------- /.github/workflows/linux.yml: -------------------------------------------------------------------------------- 1 | name: Linux 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | with: 16 | fetch-depth: 0 17 | submodules: true 18 | 19 | - name: Install Premake 20 | if: steps.cache-premake.outputs.cache-hit != 'true' 21 | run: | 22 | wget https://github.com/premake/premake-core/releases/download/v5.0.0-alpha16/premake-5.0.0-alpha16-linux.tar.gz 23 | tar -xzvf premake-5.0.0-alpha16-linux.tar.gz 24 | sudo mv premake5 /usr/local/bin/ 25 | 26 | - name: Set Enviroment Variables 27 | run: | 28 | echo "CC=clang" >> $GITHUB_ENV 29 | echo "CXX=clang++" >> $GITHUB_ENV 30 | 31 | - name: Run Premake 32 | run: premake5 gmake 33 | 34 | - name: Compile Project (Compiler) 35 | run: make -f sigma.make config=debug CC=clang 36 | 37 | - name: Compile Project (Tests) 38 | run: make -f tests.make config=debug CC=clang 39 | 40 | - name: Run Tests 41 | run: ./output/tests/bin/Debug/tests run tests ./output/compiler/bin/Debug/compiler -------------------------------------------------------------------------------- /source/ir_translator/values/variable_registry.cpp: -------------------------------------------------------------------------------- 1 | #include "variable_registry.h" 2 | 3 | namespace sigma::detail { 4 | variable_registry::variable_registry(ir::builder& builder) : m_builder(builder) {} 5 | 6 | auto variable_registry::register_variable( 7 | utility::string_table_key identifier_key, u16 size, u16 alignment 8 | ) -> handle { 9 | return m_variables[identifier_key] = m_builder.create_local(size, alignment); 10 | } 11 | 12 | auto variable_registry::create_load( 13 | utility::string_table_key identifier_key, ir::data_type type, u16 alignment 14 | ) -> handle { 15 | const auto it = m_variables.find(identifier_key); 16 | if(it != m_variables.end()) { 17 | return m_builder.create_load(it->second, type, alignment, false); 18 | } 19 | 20 | return nullptr; 21 | } 22 | 23 | void variable_registry::create_store( 24 | utility::string_table_key identifier_key, handle value, u16 alignment 25 | ) { 26 | const auto it = m_variables.find(identifier_key); 27 | if (it != m_variables.end()) { 28 | m_builder.create_store(it->second, value, alignment, false); 29 | return; 30 | } 31 | 32 | ASSERT(false, "unknown variable referenced"); 33 | } 34 | } // namespace sigma::detail 35 | -------------------------------------------------------------------------------- /source/compiler/compiler/compilation_context.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "compiler/compiler/type_system/semantic_context.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // TODO: add support for emitting to .dot files 10 | 11 | namespace sigma { 12 | struct syntax { 13 | void print_ast() const; 14 | 15 | utility::string_table strings; 16 | ast::tree ast; 17 | }; 18 | 19 | struct backend_context { 20 | backend_context(syntax& syntax, ir::target target); 21 | 22 | utility::block_allocator allocator; 23 | 24 | // TEMP: the reference here is just temporary, and will be replaced when we add support for 25 | // multiple source files 26 | syntax& syntax; 27 | semantic_context semantics; 28 | 29 | ir::module module; 30 | ir::builder builder; 31 | }; 32 | 33 | struct frontend_context { 34 | frontend_context(); 35 | 36 | void print_tokens() const; 37 | 38 | utility::block_allocator allocator; // one allocator per file 39 | 40 | token_buffer tokens; // tokenized representation of the source file 41 | syntax syntax; // ast + strings 42 | }; 43 | } // namespace sigma 44 | -------------------------------------------------------------------------------- /source/abstract_syntax_tree/tree.cpp: -------------------------------------------------------------------------------- 1 | #include "tree.h" 2 | #include 3 | 4 | namespace sigma::ast { 5 | tree::tree() : m_allocator(1024) {} 6 | 7 | void tree::add_node(handle node) { 8 | m_nodes.push_back(node); 9 | } 10 | 11 | void tree::traverse(std::function, u16)>&& function) const { 12 | utility::stack, u16>> node_stack; 13 | 14 | for(const handle& root_node : m_nodes) { 15 | node_stack.push_back({ root_node, 0 }); 16 | 17 | while (!node_stack.is_empty()) { 18 | auto [current, depth] = node_stack.pop_back(); 19 | 20 | if(!current) { 21 | continue; 22 | } 23 | 24 | function(current, depth); 25 | 26 | for (auto it = current->children.rbegin(); it != current->children.rend(); ++it) { 27 | node_stack.push_back({ *it, depth + 1 }); 28 | } 29 | } 30 | } 31 | } 32 | 33 | auto tree::allocate_node_list(u16 count) -> utility::memory_view, u16> { 34 | return { m_allocator, count }; 35 | } 36 | 37 | auto tree::get_nodes() -> utility::memory_buffer>& { 38 | return m_nodes; 39 | } 40 | 41 | auto tree::get_nodes() const -> const utility::memory_buffer>& { 42 | return m_nodes; 43 | } 44 | 45 | auto tree::get_allocator() -> utility::block_allocator& { 46 | return m_allocator; 47 | } 48 | } // namespace sigma::ast 49 | -------------------------------------------------------------------------------- /source/ir_translator/values/function_registry.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace sigma::detail { 6 | using namespace utility::types; 7 | 8 | struct external_function { 9 | handle external; 10 | ir::function_signature signature; 11 | }; 12 | 13 | class function_registry { 14 | public: 15 | function_registry(ir::builder& builder); 16 | 17 | /** 18 | * \brief Declares a new function. 19 | * \param identifier_key Unique identifier key 20 | * \param function_sig Function to declare 21 | */ 22 | void register_function( 23 | utility::string_table_key identifier_key, const ir::function_signature& function_sig 24 | ); 25 | 26 | /** 27 | * \brief Creates a call to the function located under the specified \b identifier \b key. 28 | * \param identifier_key Callee identifier key 29 | * \param parameters List of parameters to call the function with 30 | * \return Callee return value. 31 | */ 32 | [[nodiscard]] auto create_call( 33 | utility::string_table_key identifier_key, const std::vector>& parameters 34 | ) -> handle; 35 | private: 36 | ir::builder& m_builder; 37 | 38 | // TODO: the key should take parameter data types into account 39 | std::unordered_map> m_functions; 40 | std::unordered_map m_external_functions; 41 | }; 42 | } // sigma::detail 43 | -------------------------------------------------------------------------------- /source/intermediate_representation/target/system/systemv/systemv.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "intermediate_representation/target/parameter_descriptor.h" 3 | #include "intermediate_representation/target/arch/x64/x64.h" 4 | 5 | namespace sigma::ir::systemv { 6 | constexpr auto get_caller_saved() -> u16 { 7 | return 8 | static_cast(1u) << static_cast(x64::gpr::RAX) | 9 | static_cast(1u) << static_cast(x64::gpr::RDI) | 10 | static_cast(1u) << static_cast(x64::gpr::RSI) | 11 | static_cast(1u) << static_cast(x64::gpr::RCX) | 12 | static_cast(1u) << static_cast(x64::gpr::RDX) | 13 | static_cast(1u) << static_cast(x64::gpr::R8) | 14 | static_cast(1u) << static_cast(x64::gpr::R9) | 15 | static_cast(1u) << static_cast(x64::gpr::R10) | 16 | static_cast(1u) << static_cast(x64::gpr::R11); 17 | } 18 | 19 | static const parameter_descriptor parameter_descriptor = { 20 | .gpr_count = 6, 21 | .xmm_count = 4, 22 | .caller_saved_xmm_count = 5, 23 | .caller_saved_gpr_count = get_caller_saved(), 24 | .gpr_registers = { 25 | static_cast(x64::gpr::RDI), 26 | static_cast(x64::gpr::RSI), 27 | static_cast(x64::gpr::RDX), 28 | static_cast(x64::gpr::RCX), 29 | static_cast(x64::gpr::R8), 30 | static_cast(x64::gpr::R9) 31 | } 32 | }; 33 | 34 | static constexpr x64::gpr g_parameters[] = { 35 | x64::gpr::RDI, 36 | x64::gpr::RSI, 37 | x64::gpr::RDX, 38 | x64::gpr::RCX, 39 | x64::gpr::R8, 40 | x64::gpr::R9 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /source/compiler/compiler/type_system/scope.cpp: -------------------------------------------------------------------------------- 1 | #include "scope.h" 2 | 3 | namespace sigma { 4 | scope::scope(scope_type type) : scope_ty(type) {} 5 | 6 | auto scope::find_parent_namespace() const -> handle { 7 | if (parent->scope_ty == scope_type::NAMESPACE) { 8 | return parent; 9 | } 10 | 11 | return parent->find_parent_namespace(); 12 | } 13 | 14 | auto scope::find_variable(const utility::string_table_key& identifier) -> handle { 15 | const auto it = variables.find(identifier); 16 | if(it != variables.end()) { 17 | return &it->second; 18 | } 19 | 20 | if(parent) { 21 | return parent->find_variable(identifier); 22 | } 23 | 24 | return nullptr; 25 | } 26 | 27 | auto scope::find_type(const utility::string_table_key& identifier) -> handle { 28 | const auto it = types.find(identifier); 29 | if (it != types.end()) { 30 | return &it->second; 31 | } 32 | 33 | if(parent) { 34 | return parent->find_type(identifier); 35 | } 36 | 37 | return nullptr; 38 | } 39 | 40 | namespace_scope::namespace_scope(scope_type type) : scope(type) {} 41 | 42 | auto namespace_scope::find_namespace(const namespace_list& namespaces, u64 index) -> handle { 43 | if (index == namespaces.size()) { 44 | return this; 45 | } 46 | 47 | // we still have namespace directives, traverse into those scopes 48 | const auto it = child_namespaces.find(namespaces[index]); 49 | if (it != child_namespaces.end()) { 50 | return it->second->find_namespace(namespaces, index + 1); 51 | } 52 | 53 | return nullptr; 54 | } 55 | } // namespace sigma 56 | -------------------------------------------------------------------------------- /source/abstract_syntax_tree/tree.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "abstract_syntax_tree/node.h" 3 | 4 | #include 5 | #include 6 | 7 | namespace sigma::ast { 8 | class tree { 9 | public: 10 | tree(); 11 | 12 | void traverse(std::function, u16)>&& function) const; 13 | 14 | void add_node(handle node); 15 | auto allocate_node_list(u16 count) -> utility::memory_view, u16>; 16 | 17 | auto get_nodes() -> utility::memory_buffer>&; 18 | auto get_nodes() const -> const utility::memory_buffer>&; 19 | auto get_allocator() -> utility::block_allocator&; 20 | 21 | template 22 | auto create_node(node_type type, u64 child_count, handle location) -> handle { 23 | ASSERT(child_count <= std::numeric_limits::max(), "cannot allocate more than {} children", std::numeric_limits::max()); 24 | const handle node_ptr = m_allocator.emplace(); 25 | 26 | // initialize the node 27 | node_ptr->set_property(m_allocator.emplace()); 28 | node_ptr->children = allocate_node_list(static_cast(child_count)); 29 | node_ptr->location = location; 30 | node_ptr->type = type; 31 | 32 | return node_ptr; 33 | } 34 | private: 35 | // handles pointing to the main nodes (functions and globals) 36 | utility::memory_buffer> m_nodes; 37 | 38 | // the actual node data is stored in a block allocator 39 | utility::block_allocator m_allocator; 40 | }; 41 | } // namespace sigma::ast 42 | -------------------------------------------------------------------------------- /third_party/premake/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2003-2022 Jason Perkins and individual contributors. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of Premake nor the names of its contributors may be 15 | used to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /source/intermediate_representation/codegen/codegen_target.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "intermediate_representation/target/outputs/object_file_emitter.h" 3 | #include "intermediate_representation/target/arch/architecture.h" 4 | #include "intermediate_representation/module_output.h" 5 | #include "intermediate_representation/target/target.h" 6 | 7 | namespace sigma::ir { 8 | class module; 9 | 10 | class codegen_target { 11 | public: 12 | codegen_target() = default; 13 | codegen_target(target target); 14 | 15 | [[nodiscard]] auto get_target() const -> target; 16 | 17 | auto emit_bytecode(codegen_context& context) const -> utility::byte_buffer; 18 | auto get_register_intervals() const -> std::vector; 19 | void select_instructions(codegen_context& context) const; 20 | auto generate_sections(module& module) const -> module_output; 21 | auto disassemble(const utility::byte_buffer& bytecode, const codegen_context& context) const -> std::stringstream; 22 | auto emit_object_file(module& module) const -> utility::byte_buffer; 23 | private: 24 | static auto pick_object_file_emitter(system system) -> s_ptr; 25 | static auto pick_architecture(arch arch) -> s_ptr; 26 | static auto pick_disassembler(arch arch) -> s_ptr; 27 | 28 | static auto generate_windows_sections(module& module) -> module_output; 29 | static auto generate_linux_sections() -> module_output; 30 | private: 31 | s_ptr m_object_file_emitter; 32 | s_ptr m_architecture; 33 | s_ptr m_disassembler; 34 | 35 | target m_target; 36 | }; 37 | } // namespace sigma::ir 38 | -------------------------------------------------------------------------------- /documents/COMPILER_DESIGN.md: -------------------------------------------------------------------------------- 1 | # Compiler design 2 | ## Background 3 | Let 'device' refer to any processing unit which is designated for usage in kernels (ie. a GPU), and let 'host' refer to a CPU which serves as the main processing unit of the active system. 4 | 5 | ## Random notes and possible issues 6 | - Device implementation & kernels 7 | - Handling compilation - how do we handle different devices? 8 | - Handling different architecture combinations - statically compiled vs JIT (prefer statically compiled). 9 | - Something along the lines of OpenCL, but they JIT their kernels on app startup. 10 | - How unified should the systems be? 11 | - We want clear separation between device and host code. 12 | 13 | ## Nvidia compilation model 14 | - Generally speaking, we want to split the host and device codegen (host codegen can be handled by TB). 15 | - Device codegen 16 | - Device IR -> PTX assembly 17 | - Host codegen 18 | - Host IR -> assembly 19 | - After codegen finishes, we want to determine, whether any kernels are invoked 20 | - If there is at least one active kernel, we need to add CUDA context creation and deletion calls (this can be done by linking to `cuda.lib` for now). 21 | - PE emission 22 | - Focus on Windows for now. 23 | - Add a .device data segment to the PE file. 24 | - Reference specific points of the .device segment when creation kernels using `cuModuleLoadData`. 25 | - Link with `cuda.lib` and other necessary libraries. 26 | 27 | ## Links 28 | - [**CUDA PTX**](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html) 29 | - [**OpenCL**](https://www.khronos.org/opencl/) 30 | -------------------------------------------------------------------------------- /source/intermediate_representation/codegen/codegen_context.cpp: -------------------------------------------------------------------------------- 1 | #include "codegen_context.h" 2 | 3 | namespace sigma::ir { 4 | void codegen_context::append_instruction(handle instruction) { 5 | head->next_instruction = instruction; 6 | head = instruction; 7 | } 8 | 9 | void codegen_context::hint_reg(u64 interval_index, reg reg) { 10 | if (intervals[interval_index].hint.is_valid() == false) { 11 | intervals[interval_index].hint = reg; 12 | } 13 | } 14 | 15 | auto codegen_context::create_symbol_patch() const -> handle { 16 | return static_cast(function->allocator.allocate_zero(sizeof(symbol_patch))); 17 | } 18 | 19 | auto codegen_context::lookup_virtual_value(handle value) -> handle { 20 | const auto it = virtual_values.find(value->global_value_index); 21 | if(it == virtual_values.end()) { 22 | return nullptr; 23 | } 24 | 25 | return &it->second; 26 | } 27 | 28 | auto codegen_context::allocate_stack(u64 size, u64 alignment) -> i32 { 29 | stack_usage = utility::align(stack_usage + size, alignment); 30 | return -static_cast(stack_usage); 31 | } 32 | 33 | auto codegen_context::get_stack_slot(handle node) -> i32 { 34 | // check if a stack slot for the node already exists 35 | const auto it = stack_slots.find(node); 36 | 37 | // if it exists, return the stack position 38 | if (it != stack_slots.end()) { 39 | return it->second; 40 | } 41 | 42 | // allocate a new stack slot for the given node 43 | const local& local_prop = node->get(); 44 | const i32 position = allocate_stack(local_prop.size, local_prop.alignment); 45 | stack_slots[node] = position; 46 | 47 | return position; 48 | } 49 | } // namespace sigma::ir 50 | -------------------------------------------------------------------------------- /source/intermediate_representation/codegen/instruction_operand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "intermediate_representation/codegen/instruction.h" 3 | 4 | namespace sigma::ir { 5 | struct codegen_context; 6 | 7 | struct global_operand { 8 | 9 | }; 10 | 11 | /** 12 | * \brief Instruction operand, automatically compares down to its own underlying type. 13 | */ 14 | struct instruction_operand : utility::property, global_operand> { 15 | struct type { 16 | enum underlying { 17 | NONE, 18 | FLAGS, 19 | GPR, 20 | XMM, 21 | IMM, 22 | MEM, 23 | GLOBAL, 24 | ABS, 25 | LABEL 26 | }; 27 | 28 | type(); 29 | type(underlying type); 30 | 31 | [[nodiscard]] auto to_string() const -> std::string; 32 | 33 | operator underlying() const; 34 | private: 35 | underlying m_type; 36 | }; 37 | 38 | static auto create_label(const codegen_context& context, u64 target) -> handle; 39 | static auto create_global(const codegen_context& context, handle symbol, i32 disp) -> handle; 40 | static auto create_imm(const codegen_context& context,i32 imm) -> handle; 41 | static auto create_abs(const codegen_context& context,u64 abs) -> handle; 42 | 43 | auto matches(handle b) const -> bool; 44 | 45 | void set_type(type type); 46 | auto get_type() const -> type; 47 | 48 | u8 reg; 49 | u8 index; 50 | memory_scale scale; 51 | 52 | i32 immediate; // TODO: investigate whether we actually need this 53 | private: 54 | type m_type; 55 | }; 56 | 57 | bool operator==(handle operand, instruction_operand::type::underlying type); 58 | } // namespace sigma::ir 59 | -------------------------------------------------------------------------------- /source/compiler/compiler/type_system/namespace_list.cpp: -------------------------------------------------------------------------------- 1 | #include "namespace_list.h" 2 | 3 | namespace sigma { 4 | namespace_list::namespace_list(const utility::memory_view& namespaces) 5 | : namespaces(namespaces) {} 6 | 7 | auto namespace_list::empty() const -> bool { 8 | return namespaces.is_empty(); 9 | } 10 | 11 | auto namespace_list::size() const -> u64 { 12 | return namespaces.get_size(); 13 | } 14 | 15 | auto namespace_list::first() const -> utility::string_table_key { 16 | return namespaces.first(); 17 | } 18 | 19 | auto namespace_list::last() const -> utility::string_table_key { 20 | return namespaces.last(); 21 | } 22 | 23 | auto namespace_list::begin() -> base::iterator { 24 | return namespaces.begin(); 25 | } 26 | 27 | auto namespace_list::end() -> base::iterator { 28 | return namespaces.end(); 29 | } 30 | 31 | auto namespace_list::begin() const -> base::const_iterator { 32 | return namespaces.begin(); 33 | } 34 | 35 | auto namespace_list::end() const -> base::const_iterator { 36 | return namespaces.end(); 37 | } 38 | 39 | auto namespace_list::operator[](u64 index) const -> utility::string_table_key { 40 | return namespaces[index]; 41 | } 42 | 43 | auto namespace_list::operator==(const namespace_list& other) const -> bool { 44 | return namespaces == other.namespaces; 45 | } 46 | 47 | auto namespace_list::get_stream(const utility::string_table& strings) const -> std::stringstream { 48 | std::stringstream namespace_str; 49 | 50 | for (u64 i = 0; i < namespaces.get_size(); ++i) { 51 | namespace_str << strings.get(namespaces[i]); 52 | if (i + 1 < namespaces.get_size()) { 53 | namespace_str << "::"; 54 | } 55 | } 56 | 57 | return namespace_str; 58 | } 59 | } // namespace sigma 60 | -------------------------------------------------------------------------------- /source/ir_translator/values/variable_registry.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace sigma::detail { 6 | using namespace utility::types; 7 | 8 | class variable_registry { 9 | public: 10 | variable_registry(sigma::ir::builder& builder); 11 | 12 | /** 13 | * \brief Declares a new local variable. 14 | * \param identifier_key Unique identifier key 15 | * \param size Size of the variable [bytes] 16 | * \param alignment Alignment of the variable [bytes] 17 | * \return Handle representing the local variable. 18 | */ 19 | auto register_variable( 20 | utility::string_table_key identifier_key, u16 size, u16 alignment 21 | ) -> handle; 22 | 23 | /** 24 | * \brief Loads the variable located under the specified \b identifier \b key. 25 | * \param identifier_key Unique identifier key 26 | * \param type Type to interpret the variable as 27 | * \param alignment Alignment of the load operation [bytes] 28 | * \return Handle representing the loaded variable. 29 | */ 30 | auto create_load( 31 | utility::string_table_key identifier_key, ir::data_type type, u16 alignment 32 | ) -> handle; 33 | 34 | /** 35 | * \brief Stores the specified \b value in the given variable. 36 | * \param identifier_key Unique identifier key of the target variable 37 | * \param value Value to store 38 | * \param alignment Alignment of the store operation [bytes] 39 | */ 40 | void create_store( 41 | utility::string_table_key identifier_key, handle value, u16 alignment 42 | ); 43 | private: 44 | ir::builder& m_builder; 45 | std::unordered_map> m_variables; 46 | }; 47 | } // namespace sigma::detail 48 | 49 | -------------------------------------------------------------------------------- /source/tokenizer/token_buffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #include "tokenizer/token.h" 6 | 7 | namespace sigma { 8 | class token_buffer { 9 | public: 10 | auto add_token(token_info info) -> u64; 11 | 12 | [[nodiscard]] auto get_size() const -> u64; 13 | 14 | [[nodiscard]] auto get_token(u64 index) const->token; 15 | [[nodiscard]] auto get_token_symbol_key(u64 index) const -> utility::string_table_key; 16 | 17 | [[nodiscard]] auto operator[](u64 index) const -> token_info; 18 | 19 | [[nodiscard]] auto begin() const -> const token_info*; 20 | [[nodiscard]] auto end() const -> const token_info*; 21 | 22 | [[nodiscard]] auto first() const->token_info; 23 | [[nodiscard]] auto last() const -> token_info; 24 | 25 | [[nodiscard]] auto empty() const -> bool; 26 | private: 27 | utility::memory_buffer m_token_infos; 28 | }; 29 | 30 | class token_buffer_iterator { 31 | public: 32 | token_buffer_iterator(const token_buffer& tokens); 33 | 34 | void next(); 35 | 36 | void prev(); 37 | 38 | auto peek() -> token_info; 39 | auto peek_token() -> token; 40 | 41 | auto peek_next() const->token_info; 42 | auto peek_next_token() const -> token; 43 | 44 | void synchronize_indices(); 45 | 46 | auto get_current() const-> token_info; 47 | auto get_current_token() const -> token; 48 | auto get_current_token_location() const -> handle; 49 | auto get_current_token_line_index() const -> u32; 50 | auto get_current_token_char_index() const -> u32; 51 | 52 | auto get_current_peek_token() const -> token; 53 | auto get_current_peek() const -> token_info; 54 | private: 55 | const token_buffer& m_tokens; 56 | 57 | token_info m_current_info; 58 | 59 | u64 m_peek_index = 0; 60 | u64 m_index = 0; 61 | }; 62 | } // namespace sigma 63 | -------------------------------------------------------------------------------- /source/intermediate_representation/codegen/live_interval.cpp: -------------------------------------------------------------------------------- 1 | #include "live_interval.h" 2 | #include "intermediate_representation/codegen/codegen_context.h" 3 | 4 | namespace sigma::ir { 5 | auto live_interval::get_start() const -> u64 { 6 | return ranges.back().start; 7 | } 8 | 9 | auto live_interval::get_end() const -> u64 { 10 | return ranges[1].end; 11 | } 12 | 13 | void live_interval::add_range(const utility::range& range) { 14 | ASSERT(range.start <= range.end, "invalid start position"); 15 | 16 | if (!ranges.empty() && ranges.back().start <= range.end) { 17 | utility::range& top = ranges.back(); 18 | 19 | // coalesce 20 | top.start = std::min(top.start, range.start); 21 | top.end = std::max(top.end, range.end); 22 | } 23 | else { 24 | ranges.push_back(range); 25 | } 26 | } 27 | 28 | auto live_interval::split_at(codegen_context& context, u64 position) -> handle { 29 | handle interval = this; 30 | 31 | // skip past previous intervals 32 | while (interval->split_child >= 0 && position > interval->get_end()) { 33 | interval = &context.intervals[interval->split_child]; 34 | } 35 | 36 | return interval; 37 | } 38 | auto find_least_common_ancestor(handle a, handle b) -> handle { 39 | if (a == nullptr) { 40 | return b; 41 | } 42 | 43 | while (a->dominator_depth > b->dominator_depth) { 44 | a = a->dominator; 45 | } 46 | 47 | while (b->dominator_depth > a->dominator_depth) { 48 | b = b->dominator; 49 | } 50 | 51 | while (a != b) { 52 | b = b->dominator; 53 | a = a->dominator; 54 | } 55 | 56 | return a; 57 | } 58 | 59 | ptr_diff interval_intersect(handle a, handle b) { 60 | for (u64 i = a->active_range + 1; i-- > 1;) { 61 | for (u64 j = b->active_range + 1; j-- > 1;) { 62 | const ptr_diff intersect = range_intersect(a->ranges[i], b->ranges[j]); 63 | 64 | if (intersect >= 0) { 65 | return intersect; 66 | } 67 | } 68 | } 69 | 70 | return -1; 71 | } 72 | 73 | } // namespace sigma::ir 74 | -------------------------------------------------------------------------------- /source/compiler/main.cpp: -------------------------------------------------------------------------------- 1 | #include "compiler/compiler.h" 2 | #include 3 | 4 | using namespace utility::types; 5 | 6 | i32 compile(const parametric::parameters& params) { 7 | const sigma::compiler_description description { 8 | .source_path = params.get("file"), 9 | .emit_path = params.get("emit"), 10 | 11 | // default to x64 win for now 12 | .target = { 13 | params.get("arch"), 14 | params.get("system") 15 | }, 16 | }; 17 | 18 | // compile the specified description, check for errors after we finish 19 | const auto result = sigma::compiler::compile(description); 20 | 21 | if (result.has_error()) { 22 | utility::console::printerr("{}\n", result.get_error().get_message()); 23 | return 1; 24 | } 25 | 26 | return 0; 27 | } 28 | 29 | i32 show_docs(const parametric::parameters& params) { 30 | SUPPRESS_C4100(params); 31 | 32 | // TODO: add a link to the relevant github wiki 33 | const std::string link = "https://github.com/Goubermouche/sigma"; 34 | 35 | if(utility::shell::open_link(link) == 0) { 36 | return 0; 37 | } 38 | 39 | std::cerr << std::format("error: unable to open the documentation link ({})\n", link); 40 | return 1; 41 | } 42 | 43 | auto main(i32 argc, char* argv[]) -> i32 { 44 | parametric::program program; 45 | 46 | // compilation 47 | auto& compile_command = program.add_command("compile", "compile the specified source file", compile); 48 | 49 | compile_command.add_positional_argument("file", "source file to compile"); 50 | compile_command.add_flag("emit", "filepath to emit to", "e", "./a.obj"); 51 | compile_command.add_flag("arch", "CPU architecture to compile for [x64]", "", sigma::ir::arch::X64); 52 | compile_command.add_flag("system", "operating system to compile for [windows, linux]", "", sigma::ir::system::WINDOWS); 53 | 54 | // TODO: add support for emitting multiple files at once 55 | 56 | // documentation 57 | program.add_command("docs", "show project documentation", show_docs); 58 | 59 | return program.parse(argc, argv); 60 | } 61 | -------------------------------------------------------------------------------- /source/intermediate_representation/node_hierarchy/types.cpp: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include 3 | 4 | namespace sigma::ir { 5 | data_type::data_type() : m_base(base::UNKNOWN), m_bit_width(0) {} 6 | data_type::data_type(base base, u8 bit_width) : m_base(base), m_bit_width(bit_width) {} 7 | 8 | auto data_type::to_string() const -> std::string { 9 | switch (m_base) { 10 | case base::INTEGER: 11 | return "i" + std::to_string(m_bit_width); 12 | case base::POINTER: 13 | return "ptr" + std::to_string(m_bit_width); 14 | case base::FLOAT: 15 | return "f" + std::to_string(m_bit_width); 16 | case base::TUPLE: 17 | return "tuple"; 18 | case base::CONTROL: 19 | return "control"; 20 | case base::MEMORY: 21 | return "memory"; 22 | case base::UNKNOWN: 23 | return "unknown"; 24 | default: 25 | NOT_IMPLEMENTED(); 26 | return ""; 27 | } 28 | } 29 | 30 | auto data_type::is_floating_point() const -> bool { 31 | return m_base.get_underlying() == base::FLOAT; 32 | } 33 | 34 | auto data_type::is_pointer() const -> bool { 35 | return m_base.get_underlying() == base::POINTER; 36 | } 37 | 38 | auto data_type::operator==(const data_type& other) const -> bool { 39 | return m_base.get_underlying() == other.m_base.get_underlying() && m_bit_width == other.m_bit_width; 40 | } 41 | 42 | auto data_type::get_base() const -> base { 43 | return m_base; 44 | } 45 | 46 | auto data_type::get_bit_width() const -> u8 { 47 | return m_bit_width; 48 | } 49 | 50 | void data_type::set_bit_width(u8 bit_width) { 51 | m_bit_width = bit_width; 52 | } 53 | 54 | data_type::base::base() : m_type(UNKNOWN) {} 55 | 56 | data_type::base::base(underlying type) : m_type(type) {} 57 | 58 | data_type::base::operator underlying() const{ 59 | return m_type; 60 | } 61 | 62 | auto data_type::base::get_underlying() const -> underlying { 63 | return m_type; 64 | } 65 | 66 | bool operator==(data_type type_a, data_type::base::underlying type_b) { 67 | return type_a.get_base().get_underlying() == type_b; 68 | } 69 | 70 | } // namespace sigma::ir 71 | -------------------------------------------------------------------------------- /source/intermediate_representation/codegen/live_interval.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #include "intermediate_representation/node_hierarchy/node.h" 6 | #include "intermediate_representation/codegen/memory/memory.h" 7 | 8 | namespace sigma::ir { 9 | struct virtual_value { 10 | u64 use_count = std::numeric_limits::max(); 11 | reg virtual_register = reg::invalid_id; 12 | }; 13 | 14 | class use_position { 15 | public: 16 | enum type { 17 | OUT, 18 | REG, 19 | MEM_OR_REG 20 | }; 21 | 22 | u64 position; 23 | type type; 24 | }; 25 | 26 | struct phi_value { 27 | handle phi; 28 | handle target; 29 | 30 | reg destination; 31 | reg source; 32 | }; 33 | 34 | struct instruction; 35 | 36 | struct basic_block { 37 | i32 dominator_depth; 38 | u64 id; 39 | 40 | handle start; 41 | handle end; 42 | handle memory_in; 43 | 44 | handle dominator; 45 | std::unordered_set> items; 46 | }; 47 | 48 | auto find_least_common_ancestor(handle a, handle b) -> handle; 49 | 50 | struct machine_block { 51 | u64 terminator; 52 | u64 start; 53 | u64 end; 54 | 55 | handle first; 56 | handle end_node; 57 | 58 | utility::dense_set gen; 59 | utility::dense_set kill; 60 | utility::dense_set live_in; 61 | utility::dense_set live_out; 62 | }; 63 | 64 | struct symbol_patch { 65 | handle next; 66 | handle source; 67 | handle target; 68 | 69 | bool internal; 70 | u64 pos; 71 | }; 72 | 73 | struct live_interval { 74 | auto get_start() const -> u64; 75 | auto get_end() const -> u64; 76 | 77 | void add_range(const utility::range& range); 78 | auto split_at(codegen_context& context, u64 position) -> handle; 79 | 80 | reg assigned; 81 | reg hint; 82 | 83 | classified_reg reg; 84 | handle target; 85 | 86 | u64 active_range = 0; 87 | 88 | i32 data_type; // generic data type (= instruction.data_type) 89 | i32 split_child = -1; 90 | i32 spill = -1; 91 | 92 | std::vector> ranges; 93 | std::vector uses; 94 | }; 95 | 96 | ptr_diff interval_intersect(handle a, handle b); 97 | } // namespace sigma::ir 98 | -------------------------------------------------------------------------------- /source/intermediate_representation/codegen/memory/allocators/linear_scan_allocator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include "intermediate_representation/codegen/memory/allocators/allocator_base.h" 5 | #include "intermediate_representation/codegen/live_interval.h" 6 | #include "intermediate_representation/target/arch/x64/x64.h" 7 | 8 | #define REGISTER_CLASS_COUNT 2 9 | 10 | namespace sigma::ir { 11 | class linear_scan_allocator : public allocator_base { 12 | public: 13 | /** 14 | * \brief Attempts to allocate the used registers (defined by live ranges). 15 | * In the case of allocation failure the system falls back to spilling 16 | * values into the stack. 17 | * \param context Code generation context 18 | */ 19 | void allocate(codegen_context& context) override; 20 | private: 21 | void clear(); 22 | 23 | static void reverse_block_walk( 24 | codegen_context& context, handle block, handle inst 25 | ); 26 | 27 | void mark_callee_saved_constraints(const codegen_context& context); 28 | 29 | static auto partition( 30 | const std::vector& intervals, 31 | ptr_diff lo, 32 | ptr_diff hi, 33 | std::vector& arr 34 | ) -> u64; 35 | 36 | static void quick_sort_definitions( 37 | std::vector& intervals, 38 | ptr_diff lo, 39 | ptr_diff hi, 40 | std::vector& arr 41 | ); 42 | 43 | auto update_interval( 44 | const codegen_context& context, 45 | handle interval, 46 | bool is_active, 47 | u64 time, 48 | ptr_diff inactive_index 49 | ) -> bool; 50 | 51 | void move_to_active(const codegen_context& context, handle interval); 52 | void insert_split_move(codegen_context& context, u64 t, ptr_diff old_reg, ptr_diff new_reg); 53 | 54 | auto split_intersecting( 55 | codegen_context& context, 56 | u64 current_time, 57 | u64 pos, 58 | handle interval, 59 | bool is_spill 60 | ) -> u64; 61 | 62 | auto allocate_free_reg(codegen_context& context, handle interval) -> reg; 63 | auto allocate_blocked_reg(codegen_context& context, handle interval) -> reg; 64 | private: 65 | utility::dense_set m_active_set[REGISTER_CLASS_COUNT] = {}; 66 | 67 | ptr_diff m_active[REGISTER_CLASS_COUNT][16] = {}; 68 | u32 m_callee_saved[REGISTER_CLASS_COUNT] = {}; 69 | 70 | std::vector m_free_positions; 71 | std::vector m_inactive; 72 | std::vector m_unhandled; 73 | 74 | handle m_cache; 75 | }; 76 | } // namespace sigma::ir 77 | -------------------------------------------------------------------------------- /source/compiler/compiler/type_system/scope.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #include "compiler/compiler/type_system/type.h" 7 | 8 | namespace sigma { 9 | using namespace utility::types; 10 | 11 | struct variable { 12 | enum variable_flags { 13 | NONE = 0, 14 | FUNCTION_PARAMETER = 1, 15 | LOCAL = 2, 16 | GLOBAL = 4 17 | }; 18 | 19 | handle value; 20 | variable_flags flags; 21 | type type; 22 | }; 23 | 24 | FLAG_ENUM(variable::variable_flags); 25 | 26 | struct external_function { 27 | handle ir_function; 28 | ir::function_signature ir_signature; 29 | }; 30 | 31 | struct namespace_scope; 32 | 33 | struct scope { 34 | enum class scope_type : u8 { 35 | NONE = 0, 36 | REGULAR, 37 | NAMESPACE 38 | }; 39 | 40 | enum class control_type : u8 { 41 | NONE = 0, 42 | CONDITIONAL, 43 | UNCONDITIONAL 44 | }; 45 | 46 | scope() = default; 47 | scope(scope_type type); 48 | 49 | auto find_parent_namespace() const -> handle; 50 | auto find_variable(const utility::string_table_key& identifier) -> handle; 51 | auto find_type(const utility::string_table_key& identifier) -> handle; 52 | 53 | std::unordered_map variables; 54 | std::unordered_map types; 55 | 56 | handle parent = nullptr; 57 | std::vector> child_scopes; 58 | 59 | // metadata 60 | scope_type scope_ty = scope_type::NONE; 61 | control_type control = control_type::NONE; 62 | bool has_return = false; 63 | }; 64 | 65 | /** 66 | * \brief Scope representing a namespace. 67 | */ 68 | struct namespace_scope : scope { 69 | namespace_scope(scope_type type); 70 | 71 | auto find_namespace(const namespace_list& namespaces, u64 index) -> handle; 72 | 73 | // NOTE: we're using std::map instead of std::unordered_map because we need deterministic order 74 | // of elements (example: we have function A and function B, both of these have the same 75 | // cast cost, but the same one should be returned every time, not at random) 76 | 77 | // identifier -> function signature 78 | std::unordered_map>> local_functions; 79 | std::unordered_map> external_functions; 80 | 81 | // contained namespaces, regular scopes cannot contain namespaces 82 | std::unordered_map> child_namespaces; 83 | }; 84 | } // namespace sigma 85 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /source/intermediate_representation/module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "intermediate_representation/codegen/codegen_target.h" 3 | 4 | // The entire IR system is based off of an implementation in Cuik's Tilde backend 5 | // (https://github.com/RealNeGate/Cuik/tree/master/tb) 6 | 7 | // Root code generation file, contains the main module structure. Each module 8 | // contains_function a list of functions, and every function contains_function an allocator through 9 | // which it allocates and stores nodes, which represent the operation tree of the 10 | // given function. 11 | // Whilst it is possible to use the module class and its member to modify the node 12 | // tree, I'd recommend you to use the builder class (see builder.h), which provides 13 | // a more unified interface to the respective tasks. 14 | // The module class itself should only be used as a storage medium for the node 15 | // tree and for various final operations on it (compilation, emission of object 16 | // files, etc.). 17 | 18 | namespace sigma::ir { 19 | /** 20 | * \brief Basic builder which serves as a facade around the process of 21 | * constructing proper IR. 22 | */ 23 | class module { 24 | public: 25 | module(target target); 26 | ~module(); 27 | 28 | void compile() const; 29 | auto generate_object_file() -> utility::byte_buffer; 30 | 31 | auto create_external(const std::string& name, linkage linkage) -> handle; 32 | auto create_function(const function_signature& signature, linkage linkage) -> handle; 33 | 34 | auto create_global(const std::string& name, linkage linkage) -> handle; 35 | auto create_string(handle function, const std::string& value) -> handle; 36 | 37 | [[nodiscard]] auto get_target() const -> target; 38 | protected: 39 | auto get_output() -> module_output&; 40 | auto get_output() const -> const module_output&; 41 | 42 | auto generate_externals() -> std::vector>; 43 | 44 | static constexpr u8 get_text_section() { return 0; } 45 | static constexpr u8 get_data_section() { return 1; } 46 | static constexpr u8 get_rdata_section() { return 2; } 47 | static constexpr u8 get_tls_section() { return 3; } 48 | 49 | // rough memory layout: 50 | // module: 51 | // +---------------------------------------------------------+ 52 | // | functions + symbols + globals + externals + export info | 53 | // +---------------------------------------------------------+ 54 | // 55 | // functions: 56 | // +---------------------------------------+ 57 | // | nodes + users + instructions + values | 58 | // +---------------------------------------+ 59 | 60 | utility::block_allocator m_allocator; 61 | 62 | codegen_target m_codegen; 63 | module_output m_output; 64 | 65 | std::vector> m_functions; 66 | std::vector> m_symbols; 67 | std::vector> m_globals; 68 | 69 | friend class coff_file_emitter; 70 | friend class elf_file_emitter; 71 | }; 72 | } // namespace sigma::ir 73 | -------------------------------------------------------------------------------- /source/intermediate_representation/codegen/instruction_operand.cpp: -------------------------------------------------------------------------------- 1 | #include "instruction_operand.h" 2 | #include "intermediate_representation/codegen/codegen_context.h" 3 | 4 | namespace sigma::ir { 5 | auto instruction_operand::matches(handle b) const -> bool { 6 | if (m_type != b->m_type) { 7 | return false; 8 | } 9 | 10 | if (m_type == type::MEM) { 11 | return 12 | reg == b->reg && 13 | index == b->index && 14 | scale == b->scale; 15 | } 16 | 17 | return (m_type == type::GPR || m_type == type::XMM) ? reg == b->reg : false; 18 | } 19 | 20 | instruction_operand::type::type() : m_type(NONE) {} 21 | instruction_operand::type::type(underlying type) : m_type(type) {} 22 | 23 | auto instruction_operand::type::to_string() const->std::string { 24 | switch (m_type) { 25 | case NONE: return "NONE"; 26 | case FLAGS: return "FLAGS"; 27 | case GPR: return "GPR"; 28 | case XMM: return "XMM"; 29 | case IMM: return "IMM"; 30 | case MEM: return "MEM"; 31 | case GLOBAL: return "GLOBAL"; 32 | case ABS: return "ABS"; 33 | case LABEL: return "LABEL"; 34 | default: NOT_IMPLEMENTED(); 35 | } 36 | 37 | return ""; 38 | } 39 | 40 | instruction_operand::type::operator instruction_operand::type::underlying() const { 41 | return m_type; 42 | } 43 | 44 | void instruction_operand::set_type(type type) { 45 | m_type = type; 46 | } 47 | 48 | auto instruction_operand::get_type() const -> type { 49 | return m_type; 50 | } 51 | 52 | auto instruction_operand::create_label(const codegen_context& context, u64 target) -> handle { 53 | const handle operand = context.create_instruction_operand