diff --git a/premake5.lua b/premake5.lua index f293d738..d0ab1c08 100644 --- a/premake5.lua +++ b/premake5.lua @@ -11,9 +11,9 @@ workspace "sigma" "MultiProcessorCompile" } - buildoptions { "-fsanitize=address" } - linkoptions { "-fsanitize=address" } - debugformat "C7" + -- buildoptions { "-fsanitize=address" } + -- linkoptions { "-fsanitize=address" } + -- debugformat "C7" filter "configurations:Release" defines { "NDEBUG" } diff --git a/source/abstract_syntax_tree/node.cpp b/source/abstract_syntax_tree/node.cpp index 987ab8c1..ffc9b5fa 100644 --- a/source/abstract_syntax_tree/node.cpp +++ b/source/abstract_syntax_tree/node.cpp @@ -30,9 +30,7 @@ namespace sigma { case OPERATOR_DIVIDE: return "OPERATOR_DIVIDE"; case OPERATOR_MODULO: return "OPERATOR_MODULO"; - case EXPLICIT_CAST: return "EXPLICIT_CAST"; - case CAST_EXTEND: return "CAST_EXTEND"; - case CAST_TRUNCATE: return "CAST_TRUNCATE"; + case CAST: return "CAST"; case NUMERICAL_LITERAL: return "NUMERICAL_LITERAL"; case CHARACTER_LITERAL: return "CHARACTER_LITERAL"; diff --git a/source/abstract_syntax_tree/node.h b/source/abstract_syntax_tree/node.h index ce06c668..72351b0e 100644 --- a/source/abstract_syntax_tree/node.h +++ b/source/abstract_syntax_tree/node.h @@ -52,9 +52,7 @@ namespace sigma { // ast_cast // children[0] = value - EXPLICIT_CAST, - CAST_TRUNCATE, - CAST_EXTEND, + CAST, NUMERICAL_LITERAL, CHARACTER_LITERAL, @@ -123,12 +121,16 @@ namespace sigma { data_type type; }; + struct ast_array_access { + i64 stride; + }; + struct ast_namespace { utility::string_table_key identifier_key; }; using node_properties = utility::property< - ast_load, ast_sizeof, ast_function, ast_function_call, ast_literal, ast_variable, ast_bool_literal, ast_namespace, ast_cast + ast_array_access, ast_load, ast_sizeof, ast_function, ast_function_call, ast_literal, ast_variable, ast_bool_literal, ast_namespace, ast_cast >; struct sl { diff --git a/source/compiler/compiler/compiler.cpp b/source/compiler/compiler/compiler.cpp index 6cbef3c8..756e9583 100644 --- a/source/compiler/compiler/compiler.cpp +++ b/source/compiler/compiler/compiler.cpp @@ -45,8 +45,6 @@ namespace sigma { // compile the generated IR module backend.module.compile(); - std::cout << "compilation finished\n"; - //emit as an object file if(m_description.emit == OBJECT) { const filepath object_path = get_object_file_path(); diff --git a/source/compiler/compiler/type_system/semantic_context.cpp b/source/compiler/compiler/type_system/semantic_context.cpp index b9e2529b..e9879177 100644 --- a/source/compiler/compiler/type_system/semantic_context.cpp +++ b/source/compiler/compiler/type_system/semantic_context.cpp @@ -109,6 +109,13 @@ namespace sigma { return INVALID_CAST_COST; // invalid operation } + + auto determine_cast_kind(const data_type& original, const data_type& target) -> bool { + const u16 original_byte_width = original.get_byte_width(); + const u16 target_byte_width = target.get_byte_width(); + + return original_byte_width > target_byte_width; + } } // namespace detail semantic_context::semantic_context(backend_context& context) : m_context(context) { diff --git a/source/compiler/compiler/type_system/semantic_context.h b/source/compiler/compiler/type_system/semantic_context.h index 25925520..337b91d0 100644 --- a/source/compiler/compiler/type_system/semantic_context.h +++ b/source/compiler/compiler/type_system/semantic_context.h @@ -12,6 +12,14 @@ namespace sigma { auto mangle_function_identifier(const function_signature& signature, const utility::string_table& string_table) -> std::string; auto calculate_parameter_cast_cost(const function_signature& signature, const std::vector& parameter_types) -> u16; auto calculate_cast_cost(const data_type& provided, const data_type& required) -> u16; + + /** + * \brief Determine which cast should be used based on the \b original and \b target type. + * \param original Original type (type before the cast) + * \param target Target type (type after the cast) + * \return True if the cast truncates the value, false otherwise. + */ + auto determine_cast_kind(const data_type& original, const data_type& target) -> bool; } // namespace detail class semantic_context { diff --git a/source/compiler/test/main.s b/source/compiler/test/main.s index 9c2b5f55..05833102 100644 --- a/source/compiler/test/main.s +++ b/source/compiler/test/main.s @@ -12,7 +12,6 @@ // - cleanup ir gen alignment sizes (u64 vs u32 vs u16) // - unify parse_identifier_expression and parse_identifier_statement -// - heap corruption issue when indexing non-first element i32 main() { i32* memory = cast(malloc(100)); diff --git a/source/intermediate_representation/builder.cpp b/source/intermediate_representation/builder.cpp index 975eb334..a0bc13b2 100644 --- a/source/intermediate_representation/builder.cpp +++ b/source/intermediate_representation/builder.cpp @@ -1,11 +1,12 @@ #include "builder.h" +#include // #define DEBUG_PRINT_BUILDER #ifdef DEBUG_PRINT_BUILDER -#define DEBUG_PRINT(__message) utility::console::print("{}\n", (__message)) +#define DEBUG_PRINT(__fmt, ...) utility::console::print("{}\n", std::format(__fmt, ##__VA_ARGS__)) #else -#define DEBUG_PRINT(__message) +#define DEBUG_PRINT(__fmt, ...) #endif namespace sigma::ir { @@ -68,22 +69,22 @@ namespace sigma::ir { } auto builder::create_local(u16 size, u16 alignment) const -> handle { - DEBUG_PRINT("creating local"); + DEBUG_PRINT("creating local size: {} alignment: {}", size, alignment); return get_insert_point_checked()->create_local(size, alignment); } auto builder::create_string(const std::string& value) const -> handle { - DEBUG_PRINT("creating string"); + DEBUG_PRINT("creating string '{}'", utility::detail::escape_string(value)); return m_target.create_string(get_insert_point_checked(), value); } auto builder::create_signed_integer(i64 value, u8 bit_width) const -> handle { - DEBUG_PRINT("creating signed integer"); + DEBUG_PRINT("creating signed integer i{} {}", bit_width, value); return get_insert_point_checked()->create_signed_integer(value, bit_width); } auto builder::create_unsigned_integer(u64 value, u8 bit_width) const -> handle { - DEBUG_PRINT("creating unsigned integer"); + DEBUG_PRINT("creating unsigned integer u{} {}", bit_width, value); return get_insert_point_checked()->create_unsigned_integer(value, bit_width); } @@ -123,17 +124,17 @@ namespace sigma::ir { } void builder::create_store(handle destination, handle value, u32 alignment, bool is_volatile) const { - DEBUG_PRINT("creating store"); + DEBUG_PRINT("creating store alignment: {}", alignment); get_insert_point_checked()->create_store(destination, value, alignment, is_volatile); } auto builder::create_array_access(handle base, handle index, i64 stride) const -> handle { - DEBUG_PRINT("creating array access"); + DEBUG_PRINT("creating array access stride: {}", stride); return get_insert_point_checked()->create_array_access(base, index, stride); } auto builder::create_load(handle value_to_load, data_type data_type, u32 alignment, bool is_volatile) const -> handle { - DEBUG_PRINT("creating load"); + DEBUG_PRINT("creating load type: {} alignment: {}", data_type.to_string(), alignment); return get_insert_point_checked()->create_load(value_to_load, data_type, alignment, is_volatile); } diff --git a/source/intermediate_representation/module.cpp b/source/intermediate_representation/module.cpp index b13560ce..52156399 100644 --- a/source/intermediate_representation/module.cpp +++ b/source/intermediate_representation/module.cpp @@ -26,7 +26,7 @@ namespace sigma::ir { // specify individual optimization passes const optimization_pass_list optimizations({}); const auto register_allocator = std::make_shared(); - utility::string assembly; + // utility::string assembly; // go through all declared functions and run codegen for (const handle function : m_functions) { @@ -70,7 +70,7 @@ namespace sigma::ir { const utility::byte_buffer bytecode = m_codegen.emit_bytecode(codegen); // DEBUG - assembly.append(m_codegen.disassemble(bytecode, codegen)); + // assembly.append(m_codegen.disassemble(bytecode, codegen)); // finally, emit the compiled function function->output = { @@ -85,7 +85,7 @@ namespace sigma::ir { } // DEBUG - utility::console::print("{}\n", assembly.get_underlying()); + // utility::console::print("{}\n", assembly.get_underlying()); } auto module::generate_object_file() -> utility::byte_buffer { diff --git a/source/intermediate_representation/target/arch/x64/x64_instruction_selection.cpp b/source/intermediate_representation/target/arch/x64/x64_instruction_selection.cpp index c790570c..7778e1ff 100644 --- a/source/intermediate_representation/target/arch/x64/x64_instruction_selection.cpp +++ b/source/intermediate_representation/target/arch/x64/x64_instruction_selection.cpp @@ -1113,7 +1113,6 @@ namespace sigma::ir { } auto x64_architecture::select_memory_access_instruction(codegen_context& context, handle n, reg dst, i32 store_op, i32 src) -> handle { - std::cout << "ENTER" << std::endl; const bool has_second_in = store_op < 0 && src >= 0; i32 offset = 0; @@ -1147,7 +1146,6 @@ namespace sigma::ir { } index = allocate_node_register(context, n).id; - std::cout << "STRIDE: " << stride << '\n'; if(stride == 1) { // no scaling required @@ -1162,7 +1160,10 @@ namespace sigma::ir { } // we can't fit this into an LEA, might as well just do a shift - context.append_instruction(create_rri(context, instruction::type::SHL, I64_TYPE, dst, index, static_cast(scale))); + context.append_instruction(create_rri( + context, instruction::type::SHL, I64_TYPE, dst, static_cast(index), static_cast(scale) + )); + index = dst.id; scale = static_cast(0); } @@ -1173,7 +1174,10 @@ namespace sigma::ir { dst = allocate_virtual_register(context, nullptr, I64_TYPE); } - context.append_instruction(create_rri(context, instruction::type::IMUL, I64_TYPE, dst, index, stride)); + context.append_instruction(create_rri( + context, instruction::type::IMUL, I64_TYPE, dst, static_cast(index), static_cast(stride) + )); + index = dst.id; } @@ -1192,8 +1196,6 @@ namespace sigma::ir { base = allocate_node_register(context, n); } - std::cout << "EXIT" << std::endl; - // compute the base if(store_op < 0) { if(has_second_in) { diff --git a/source/ir_translator/ir_translator.cpp b/source/ir_translator/ir_translator.cpp index ac786c9b..8f94273e 100644 --- a/source/ir_translator/ir_translator.cpp +++ b/source/ir_translator/ir_translator.cpp @@ -40,9 +40,7 @@ namespace sigma { case node_type::OPERATOR_DIVIDE: case node_type::OPERATOR_MODULO: return translate_binary_math_operator(ast_node); - case node_type::CAST_EXTEND: - case node_type::CAST_TRUNCATE: - case node_type::EXPLICIT_CAST: return translate_cast(ast_node); + case node_type::CAST: return translate_cast(ast_node); // literals case node_type::NUMERICAL_LITERAL: return translate_numerical_literal(ast_node); @@ -191,10 +189,9 @@ namespace sigma { const handle base = translate_node(access_node->children[0]); const handle index = translate_node(access_node->children[1]); - // TODO: calculate this in the type checker - // should be the size of the type for 1D arrays - constexpr i64 stride = 4; - + // stride calculated by the type checker + const i64 stride = access_node->get().stride; + return m_context.builder.create_array_access(base, index, stride); } @@ -231,8 +228,7 @@ namespace sigma { default: PANIC("unexpected node type '{}' received", operator_node->type.to_string()); } - NOT_IMPLEMENTED(); - return nullptr; + return nullptr; // unreachable } auto ir_translator::translate_cast(handle cast_node) -> handle { @@ -245,22 +241,18 @@ namespace sigma { return value_to_cast; // just return the pointer } - if(cast_node->type == node_type::CAST_TRUNCATE) { + if(detail::determine_cast_kind(cast.original_type, cast.target_type)) { + // truncate the original value return m_context.builder.create_truncate(value_to_cast, target_type); } - if(cast_node->type == node_type::CAST_EXTEND) { - if(cast.original_type.is_signed()) { - // signed extend - return m_context.builder.create_sxt(value_to_cast, target_type); - } - - // zero extend - return m_context.builder.create_zxt(value_to_cast, target_type); + if (cast.original_type.is_signed()) { + // sign-extend the original value + return m_context.builder.create_sxt(value_to_cast, target_type); } - NOT_IMPLEMENTED(); - return nullptr; + // zero-extend the original value + return m_context.builder.create_zxt(value_to_cast, target_type); } auto ir_translator::translate_function_call(handle call_node) -> handle { @@ -278,34 +270,37 @@ namespace sigma { } auto ir_translator::translate_load(handle load_node) -> handle { + // translate the target value const handle value_to_load = translate_node(load_node->children[0]); + + // get the type and alignment of the load const data_type& type_to_load = load_node->get().type; const ir::data_type ir_type = detail::data_type_to_ir(type_to_load); const u16 alignment = type_to_load.get_byte_width(); - const handle load = m_context.builder.create_load(value_to_load, ir_type, alignment, false); - - return load; + // create the load operation + return m_context.builder.create_load(value_to_load, ir_type, alignment, false); } auto ir_translator::translate_variable_access(handle access_node) const -> handle { const auto& accessed_variable = access_node->get(); - const auto variable = m_context.semantics.get_variable(accessed_variable.identifier_key); - return variable->value; + // just return the variable value, at this point everything was handled by the type checker + return m_context.semantics.get_variable(accessed_variable.identifier_key)->value; } auto ir_translator::translate_variable_assignment(handle assignment_node) -> handle { - const auto& var = assignment_node->children[0]->get(); - const u16 alignment = var.type.get_byte_width(); + const auto& variable = assignment_node->children[0]->get(); + const u16 alignment = variable.type.get_byte_width(); - const handle value = translate_node(assignment_node->children[1]); + // get the value we want to store + const handle value_to_store = translate_node(assignment_node->children[1]); // get the storage location const handle storage_node = assignment_node->children[0]; const handle storage = translate_node(storage_node); - m_context.builder.create_store(storage, value, alignment, false); + m_context.builder.create_store(storage, value_to_store, alignment, false); return nullptr; } diff --git a/source/parser/parser.cpp b/source/parser/parser.cpp index fb9447f3..1f33c69b 100644 --- a/source/parser/parser.cpp +++ b/source/parser/parser.cpp @@ -348,7 +348,7 @@ namespace sigma { m_tokens.next(); - array_access = create_node( + array_access = create_node( node_type::ARRAY_ACCESS, index_expressions.size() + 1, location ); @@ -659,7 +659,7 @@ namespace sigma { m_tokens.next(); // prime the next token (probably a SEMICOLON) // create the cast node - const handle cast_node = create_node(node_type::EXPLICIT_CAST, 1, location); + const handle cast_node = create_node(node_type::CAST, 1, location); cast_node->children[0] = value_to_cast; ast_cast& cast = cast_node->get(); @@ -898,7 +898,6 @@ namespace sigma { // parse an array access if(m_tokens.get_current_token() == token_type::LEFT_BRACKET) { - const handle location = m_tokens.get_current_token_location(); std::vector> index_expressions; while (true) { @@ -916,7 +915,7 @@ namespace sigma { m_tokens.next(); - const handle array_access = create_node( + const handle array_access = create_node( node_type::ARRAY_ACCESS, index_expressions.size() + 1, location ); diff --git a/source/type_checker/type_checker.cpp b/source/type_checker/type_checker.cpp index 46c20d3e..07bd9730 100644 --- a/source/type_checker/type_checker.cpp +++ b/source/type_checker/type_checker.cpp @@ -26,7 +26,9 @@ namespace sigma { case node_type::FUNCTION_CALL: return type_check_function_call(ast_node, parent, expected); case node_type::NAMESPACE_DECLARATION: return type_check_namespace_declaration(ast_node, expected); - case node_type::EXPLICIT_CAST: return type_check_explicit_cast(ast_node, parent, expected); + + // since implicit casts are not type checked we can interpret these casts a being explicit + case node_type::CAST: return type_check_explicit_cast(ast_node, parent, expected); case node_type::SIZEOF: return type_check_sizeof(ast_node, parent, expected); // control flow @@ -38,11 +40,11 @@ namespace sigma { case node_type::VARIABLE_DECLARATION: return type_check_variable_declaration(ast_node); case node_type::VARIABLE_ASSIGNMENT: return type_check_variable_assignment(ast_node); + case node_type::LOAD: return type_check_load(ast_node, expected); + case node_type::VARIABLE_ACCESS: return type_check_variable_access(ast_node, parent, expected); case node_type::ARRAY_ACCESS: return type_check_array_access(ast_node, parent, expected); - case node_type::LOAD: return type_check_load(ast_node, parent, expected); - // binary operators case node_type::OPERATOR_ADD: case node_type::OPERATOR_SUBTRACT: @@ -249,10 +251,14 @@ namespace sigma { TRY(type_check_node(access_node->children[i + 1], access_node, data_type::create_u64())); } - return array_type.create_access(static_cast(access_level)); + // implicitly cast the type, more of a sanity check + TRY(const data_type accessed_type, implicit_type_cast(array_type.create_access(static_cast(access_level)), expected, parent, access_node)); + access_node->get().stride = accessed_type.get_byte_width(); + + return accessed_type; } - auto type_checker::type_check_load(handle load_node, handle parent, data_type expected) -> utility::result { + auto type_checker::type_check_load(handle load_node, data_type expected) -> utility::result { TRY(const data_type load_type, type_check_node(load_node->children[0], load_node, expected)); load_node->get().type = load_type; return load_type; @@ -382,10 +388,10 @@ namespace sigma { // | | // | --> cast // | | - // target target + // target target const bool truncate = original_byte_width > target_byte_width; - const handle cast_node = m_context.syntax.ast.create_node(truncate ? node_type::CAST_TRUNCATE : node_type::CAST_EXTEND, 1, nullptr); + const handle cast_node = m_context.syntax.ast.create_node(node_type::CAST, 1, nullptr); ast_cast& cast = cast_node->get(); cast.original_type = original_type; cast.target_type = target_type; @@ -432,7 +438,7 @@ namespace sigma { return error::emit(error::code::UNKNOWN_VARIABLE, access_node->location, identifier_str); } - // default to the declared type + // default to the expected type TRY(access.type, implicit_type_cast(variable->type, expected, parent, access_node)); return access.type; // return the type checked value } diff --git a/source/type_checker/type_checker.h b/source/type_checker/type_checker.h index f48c8609..06e81904 100644 --- a/source/type_checker/type_checker.h +++ b/source/type_checker/type_checker.h @@ -41,10 +41,11 @@ namespace sigma { auto type_check_branch(handle branch_node) -> utility::result; auto type_check_binary_math_operator(handle operator_node, data_type expected) -> utility::result; auto type_check_explicit_cast(handle cast_node, handle parent, data_type expected) -> utility::result; - auto type_check_variable_access(handle access_node, handle parent, data_type expected) const-> utility::result; - auto type_check_array_access(handle access_node, handle parent, data_type expected) -> utility::result; auto type_check_variable_assignment(handle assignment_node) -> utility::result; - auto type_check_load(handle load_node, handle parent, data_type expected) -> utility::result; + auto type_check_load(handle load_node, data_type expected) -> utility::result; + + auto type_check_array_access(handle access_node, handle parent, data_type expected) -> utility::result; + auto type_check_variable_access(handle access_node, handle parent, data_type expected) const->utility::result; auto type_check_numerical_literal(handle literal_node, data_type expected) const-> utility::result; auto type_check_character_literal(handle literal_node, handle parent, data_type expected) const-> utility::result; diff --git a/source/utility/string_helper.h b/source/utility/string_helper.h index d424ad1a..c219ff6b 100644 --- a/source/utility/string_helper.h +++ b/source/utility/string_helper.h @@ -79,7 +79,7 @@ namespace utility::detail { overflowed = stream.fail() || value > std::numeric_limits::max() || value < std::numeric_limits::min(); return static_cast(value); } - if constexpr (std::is_signed_v) { + else if constexpr (std::is_signed_v) { // read directly type value; stream >> value;