Skip to content

Commit

Permalink
Added basic support for 1D arrays.
Browse files Browse the repository at this point in the history
Goubermouche committed Jan 26, 2024
1 parent 49dfd72 commit bfc7d94
Showing 15 changed files with 92 additions and 76 deletions.
6 changes: 3 additions & 3 deletions premake5.lua
Original file line number Diff line number Diff line change
@@ -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" }
4 changes: 1 addition & 3 deletions source/abstract_syntax_tree/node.cpp
Original file line number Diff line number Diff line change
@@ -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";
10 changes: 6 additions & 4 deletions source/abstract_syntax_tree/node.h
Original file line number Diff line number Diff line change
@@ -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 {
2 changes: 0 additions & 2 deletions source/compiler/compiler/compiler.cpp
Original file line number Diff line number Diff line change
@@ -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();
7 changes: 7 additions & 0 deletions source/compiler/compiler/type_system/semantic_context.cpp
Original file line number Diff line number Diff line change
@@ -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) {
8 changes: 8 additions & 0 deletions source/compiler/compiler/type_system/semantic_context.h
Original file line number Diff line number Diff line change
@@ -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<data_type>& 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 {
1 change: 0 additions & 1 deletion source/compiler/test/main.s
Original file line number Diff line number Diff line change
@@ -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<i32*>(malloc(100));
19 changes: 10 additions & 9 deletions source/intermediate_representation/builder.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
#include "builder.h"
#include <utility/string_helper.h>

// #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<node> {
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<node> {
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<node> {
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<node> {
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<node> destination, handle<node> 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<node> base, handle<node> index, i64 stride) const -> handle<node> {
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<node> value_to_load, data_type data_type, u32 alignment, bool is_volatile) const -> handle<node> {
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);
}

6 changes: 3 additions & 3 deletions source/intermediate_representation/module.cpp
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@ namespace sigma::ir {
// specify individual optimization passes
const optimization_pass_list optimizations({});
const auto register_allocator = std::make_shared<linear_scan_allocator>();
utility::string assembly;
// utility::string assembly;

// go through all declared functions and run codegen
for (const handle<function> 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 {
Original file line number Diff line number Diff line change
@@ -1113,7 +1113,6 @@ namespace sigma::ir {
}

auto x64_architecture::select_memory_access_instruction(codegen_context& context, handle<node> n, reg dst, i32 store_op, i32 src) -> handle<instruction> {
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<i32>(scale)));
context.append_instruction(create_rri(
context, instruction::type::SHL, I64_TYPE, dst, static_cast<reg::id_type>(index), static_cast<i32>(scale)
));

index = dst.id;
scale = static_cast<memory_scale>(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<reg::id_type>(index), static_cast<i32>(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) {
53 changes: 24 additions & 29 deletions source/ir_translator/ir_translator.cpp
Original file line number Diff line number Diff line change
@@ -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<ir::node> base = translate_node(access_node->children[0]);
const handle<ir::node> 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<ast_array_access>().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<node> cast_node) -> handle<ir::node> {
@@ -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<node> call_node) -> handle<ir::node> {
@@ -278,34 +270,37 @@ namespace sigma {
}

auto ir_translator::translate_load(handle<node> load_node) -> handle<ir::node> {
// translate the target value
const handle<ir::node> 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<ast_load>().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<ir::node> 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<node> access_node) const -> handle<ir::node> {
const auto& accessed_variable = access_node->get<ast_variable>();

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<node> assignment_node) -> handle<ir::node> {
const auto& var = assignment_node->children[0]->get<ast_variable>();
const u16 alignment = var.type.get_byte_width();
const auto& variable = assignment_node->children[0]->get<ast_variable>();
const u16 alignment = variable.type.get_byte_width();

const handle<ir::node> value = translate_node(assignment_node->children[1]);
// get the value we want to store
const handle<ir::node> value_to_store = translate_node(assignment_node->children[1]);

// get the storage location
const handle<node> storage_node = assignment_node->children[0];
const handle<ir::node> 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;
}

7 changes: 3 additions & 4 deletions source/parser/parser.cpp
Original file line number Diff line number Diff line change
@@ -348,7 +348,7 @@ namespace sigma {

m_tokens.next();

array_access = create_node<utility::empty_property>(
array_access = create_node<ast_array_access>(
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<node> cast_node = create_node<ast_cast>(node_type::EXPLICIT_CAST, 1, location);
const handle<node> cast_node = create_node<ast_cast>(node_type::CAST, 1, location);
cast_node->children[0] = value_to_cast;

ast_cast& cast = cast_node->get<ast_cast>();
@@ -898,7 +898,6 @@ namespace sigma {

// parse an array access
if(m_tokens.get_current_token() == token_type::LEFT_BRACKET) {
const handle<token_location> location = m_tokens.get_current_token_location();
std::vector<handle<node>> index_expressions;

while (true) {
@@ -916,7 +915,7 @@ namespace sigma {

m_tokens.next();

const handle<node> array_access = create_node<utility::empty_property>(
const handle<node> array_access = create_node<ast_array_access>(
node_type::ARRAY_ACCESS, index_expressions.size() + 1, location
);

22 changes: 14 additions & 8 deletions source/type_checker/type_checker.cpp
Original file line number Diff line number Diff line change
@@ -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<u8>(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<u8>(access_level)), expected, parent, access_node));
access_node->get<ast_array_access>().stride = accessed_type.get_byte_width();

return accessed_type;
}

auto type_checker::type_check_load(handle<node> load_node, handle<node> parent, data_type expected) -> utility::result<data_type> {
auto type_checker::type_check_load(handle<node> load_node, data_type expected) -> utility::result<data_type> {
TRY(const data_type load_type, type_check_node(load_node->children[0], load_node, expected));
load_node->get<ast_load>().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<node> cast_node = m_context.syntax.ast.create_node<ast_cast>(truncate ? node_type::CAST_TRUNCATE : node_type::CAST_EXTEND, 1, nullptr);
const handle<node> cast_node = m_context.syntax.ast.create_node<ast_cast>(node_type::CAST, 1, nullptr);
ast_cast& cast = cast_node->get<ast_cast>();
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
}
7 changes: 4 additions & 3 deletions source/type_checker/type_checker.h
Original file line number Diff line number Diff line change
@@ -41,10 +41,11 @@ namespace sigma {
auto type_check_branch(handle<node> branch_node) -> utility::result<data_type>;
auto type_check_binary_math_operator(handle<node> operator_node, data_type expected) -> utility::result<data_type>;
auto type_check_explicit_cast(handle<node> cast_node, handle<node> parent, data_type expected) -> utility::result<data_type>;
auto type_check_variable_access(handle<node> access_node, handle<node> parent, data_type expected) const-> utility::result<data_type>;
auto type_check_array_access(handle<node> access_node, handle<node> parent, data_type expected) -> utility::result<data_type>;
auto type_check_variable_assignment(handle<node> assignment_node) -> utility::result<data_type>;
auto type_check_load(handle<node> load_node, handle<node> parent, data_type expected) -> utility::result<data_type>;
auto type_check_load(handle<node> load_node, data_type expected) -> utility::result<data_type>;

auto type_check_array_access(handle<node> access_node, handle<node> parent, data_type expected) -> utility::result<data_type>;
auto type_check_variable_access(handle<node> access_node, handle<node> parent, data_type expected) const->utility::result<data_type>;

auto type_check_numerical_literal(handle<node> literal_node, data_type expected) const-> utility::result<data_type>;
auto type_check_character_literal(handle<node> literal_node, handle<node> parent, data_type expected) const-> utility::result<data_type>;
2 changes: 1 addition & 1 deletion source/utility/string_helper.h
Original file line number Diff line number Diff line change
@@ -79,7 +79,7 @@ namespace utility::detail {
overflowed = stream.fail() || value > std::numeric_limits<type>::max() || value < std::numeric_limits<type>::min();
return static_cast<type>(value);
}
if constexpr (std::is_signed_v<type>) {
else if constexpr (std::is_signed_v<type>) {
// read directly
type value;
stream >> value;

0 comments on commit bfc7d94

Please sign in to comment.