diff --git a/bootstrap/main.cpp b/bootstrap/main.cpp index b92362a..bddb533 100644 --- a/bootstrap/main.cpp +++ b/bootstrap/main.cpp @@ -1146,68 +1146,6 @@ enum class Side : u8 right, }; -struct NodeDataType -{ - enum class Id : u8 - { - VOID, - INTEGER, - TUPLE, - CONTROL, - MEMORY, - POINTER, - }; - Id id; - u8 bit_count:5; -}; - -union AbiInfoPayload -{ - NodeDataType direct; - NodeDataType direct_pair[2]; - NodeDataType direct_coerce; - struct - { - NodeDataType type; - u32 alignment; - } indirect; -}; -typedef union AbiInfoPayload AbiInfoPayload; - -struct AbiInfoAttributes -{ - u8 by_reg: 1; - u8 zero_extend: 1; - u8 sign_extend: 1; - u8 realign: 1; - u8 by_value: 1; -}; -typedef struct AbiInfoAttributes AbiInfoAttributes; - -struct AbiInfo -{ - AbiInfoPayload payload; - u16 indices[2]; - AbiInfoAttributes attributes; - AbiInfoKind kind; -}; - -struct FunctionPrototype -{ - AbiInfo* argument_type_abis; // The count for this array is "original_argument_count", not "abi_argument_count" - SemaType** original_argument_types; - // TODO: are these needed? - // Node::DataType* abi_argument_types; - // u32 abi_argument_count; - SemaType* original_return_type; - AbiInfo return_type_abi; - u32 original_argument_count; - // TODO: is this needed? - // Node::DataType abi_return_type; - u8 varags:1; -}; - -struct Function; global auto constexpr void_type_index = 0; global auto constexpr noreturn_type_index = 1; @@ -1218,6 +1156,7 @@ global auto constexpr integer_type_offset = 5; global auto constexpr integer_type_count = 64 * 2; global auto constexpr builtin_type_count = integer_type_count + integer_type_offset + 1; +struct Function; struct Thread { Arena* arena; @@ -1242,30 +1181,123 @@ struct Unit } }; - struct Node; +struct FunctionPrototype; + +struct NodeType +{ + enum class Id: u8 + { + INVALID, + BOTTOM, + TOP, + VOID, + INTEGER, + TUPLE, + CONTROL, + MEMORY, + POINTER, + }; + Id id; + union + { + struct + { + u64 constant; + u8 bit_count; + u8 is_constant; + } integer; + }; + u8 equal(NodeType other) + { + if (id != other.id) + { + return 0; + } + + switch (id) + { + default: trap(); + } + } + + u8 is_constant() + { + switch (id) + { + case Id::VOID: + case Id::INTEGER: + return 1; + default: + trap(); + } + } +}; + +union AbiInfoPayload +{ + NodeType direct; + NodeType direct_pair[2]; + NodeType direct_coerce; + struct + { + NodeType type; + u32 alignment; + } indirect; +}; +typedef union AbiInfoPayload AbiInfoPayload; + +struct AbiInfoAttributes +{ + u8 by_reg: 1; + u8 zero_extend: 1; + u8 sign_extend: 1; + u8 realign: 1; + u8 by_value: 1; +}; +typedef struct AbiInfoAttributes AbiInfoAttributes; + +struct AbiInfo +{ + AbiInfoPayload payload; + u16 indices[2]; + AbiInfoAttributes attributes; + AbiInfoKind kind; +}; struct Function { + struct Prototype + { + AbiInfo* argument_type_abis; // The count for this array is "original_argument_count", not "abi_argument_count" + SemaType** original_argument_types; + // TODO: are these needed? + // Node::DataType* abi_argument_types; + // u32 abi_argument_count; + SemaType* original_return_type; + AbiInfo return_type_abi; + u32 original_argument_count; + // TODO: is this needed? + // Node::DataType abi_return_type; + u8 varags:1; + }; Symbol symbol; Node* root_node; Node** parameters; - FunctionPrototype prototype; + Function::Prototype prototype; u32 node_count; u16 parameter_count; }; -struct ProjectionData +struct ConstantIntData { - NodeDataType type; - u16 index; + u64 value; + Node* input; + u32 gvn; + u8 bit_count; }; -struct Output -{ - Node* node; - u16 slot; -}; +[[nodiscard]] fn Node* add_constant_integer(Arena* arena, ConstantIntData data); // This is a node in the "sea of nodes" sense: // https://en.wikipedia.org/wiki/Sea_of_nodes @@ -1277,18 +1309,27 @@ struct Node PROJECTION, RETURN, CONSTANT_INT, + INT_ADD, + INT_SUB, }; - static_assert(sizeof(NodeDataType) <= 2); + using Type = NodeType; + + + struct Output + { + Node* node; + u16 slot; + }; Node** inputs; Output* outputs; u32 gvn; + Type type; u16 input_count; u16 input_capacity; u16 output_count; u16 output_capacity; - NodeDataType data_type; Id id; union @@ -1297,7 +1338,6 @@ struct Node { u32 index; } projection; - u64 constant_int; }; forceinline Slice get_inputs() @@ -1318,7 +1358,7 @@ struct Node struct NodeData { - NodeDataType type; + Type type; u16 input_count; Id id; }; @@ -1339,11 +1379,11 @@ struct Node .inputs = arena->allocate_many(data.input_capacity), .outputs = arena->allocate_many(output_capacity), .gvn = data.gvn, + .type = data.s.type, .input_count = data.s.input_count, .input_capacity = data.input_capacity, .output_count = output_count, .output_capacity = output_capacity, - .data_type = data.s.type, .id = data.s.id, }; @@ -1370,41 +1410,53 @@ struct Node { return add_from_function_dynamic(arena, function, data, data.input_count); } + + struct ProjectionData + { + Node::Type type; + u16 index; + }; [[nodiscard]] Node* project(Arena* arena, Function* function, ProjectionData data) { - assert(data_type.id == NodeDataType::Id::TUPLE); + assert(type.id == Type::Id::TUPLE); Node* projection = Node::add_from_function(arena, function, { .input_count = 1, }); assert(projection != this); projection->id = Node::Id::PROJECTION; - projection->data_type = data.type; + projection->type = data.type; // projection->reallocate_edges(unit, 4); projection->input_count = 1; - projection->set_input(this, 0); + projection->set_input(arena, this, 0); projection->projection.index = data.index; return projection; } - void set_input(Node* input, u16 slot) + void set_input(Arena* arena, Node* input, u16 slot) { assert(slot < input_count); remove_output(slot); inputs[slot] = input; if (input) { - add_output(input, slot); + add_output(arena, input, slot); } } - void add_output(Node* input, u16 slot) + void add_output(Arena* arena, Node* input, u16 slot) { if (input->output_count >= input->output_capacity) { - trap(); + auto new_capacity = max(input->output_count, (u32)input->output_capacity * 2); + assert(new_capacity <= 0xffff); + auto* new_array = arena->allocate_many(new_capacity); + memcpy(new_array, input->outputs, sizeof(Output) * input->output_count); + memset(new_array + input->output_count, 0, sizeof(Output) * (new_capacity - input->output_count)); + input->outputs = new_array; + input->output_capacity = new_capacity; } auto index = input->output_count; @@ -1439,6 +1491,9 @@ struct Node case Id::PROJECTION: case Id::CONSTANT_INT: break; + case Id::INT_ADD: + case Id::INT_SUB: + trap(); } return is_good_id | is_projection() | cfg_is_control_projection(); @@ -1457,16 +1512,16 @@ struct Node u8 cfg_is_control_projection() { - return is_projection() & (data_type.id == NodeDataType::Id::CONTROL); + return is_projection() & (type.id == Node::Type::Id::CONTROL); } u8 is_cfg_control() { - switch (data_type.id) + switch (type.id) { - case NodeDataType::Id::CONTROL: + case Node::Type::Id::CONTROL: return 1; - case NodeDataType::Id::TUPLE: + case Node::Type::Id::TUPLE: for (Output& output : get_outputs()) { if (output.node->cfg_is_control_projection()) @@ -1478,8 +1533,164 @@ struct Node return 0; } } + + Node* idealize() + { + switch (id) + { + case Id::ROOT: + case Id::PROJECTION: + case Id::RETURN: + case Id::CONSTANT_INT: + case Id::INT_ADD: + case Id::INT_SUB: + return 0; + } + } + + u8 is_unused() + { + return output_count == 0; + } + + u8 is_dead() + { + return is_unused() & (input_count == 0) & (type.id == Node::Type::Id::INVALID); + } + + void kill(Arena* arena) + { + assert(is_unused()); + + for (u16 i = 0; i < input_count; i += 1) + { + set_input(arena, 0, i); + } + input_count = 0; + type = {}; + + assert(is_dead()); + } + + static auto constexpr enable_peephole = 1; + Node* peephole(Arena* arena, Function* function) + { + Node::Type type = this->type = compute(); + + if (!enable_peephole) + { + return this; + } + + if ((!is_constant()) & type.is_constant()) + { + this->kill(arena); + auto gvn = function->node_count; + function->node_count += 1; + auto* constant_int = Node::add(arena, { + .s = + { + .type = type, + .input_count = 1, + .id = Node::Id::CONSTANT_INT, + }, + .gvn = gvn, + .input_capacity = 1, + }); + constant_int->set_input(arena, function->root_node, 0); + auto* result = constant_int->peephole(arena, function); + return result; + } + + Node* n = idealize(); + return n ? n : this; + } + + u8 is_constant() + { + switch (id) + { + default: + return 0; + case Id::CONSTANT_INT: + return 1; + } + } + + Node::Type compute() + { + switch (id) + { + case Node::Id::INT_ADD: + case Node::Id::INT_SUB: + { + auto left_type = inputs[1]->type; + auto right_type = inputs[2]->type; + if ((left_type.id == Node::Type::Id::INTEGER) & (right_type.id == Node::Type::Id::INTEGER)) + { + if (left_type.is_constant() & right_type.is_constant()) + { + u64 result; + switch (id) + { + case Id::ROOT: + case Id::PROJECTION: + case Id::RETURN: + case Id::CONSTANT_INT: + trap(); + case Id::INT_ADD: + result = left_type.integer.constant + right_type.integer.constant; + break; + case Id::INT_SUB: + result = left_type.integer.constant - right_type.integer.constant; + break; + } + + + return Node::Type{ + .id = Node::Type::Id::INTEGER, + .integer = { + .constant = result, + .bit_count = left_type.integer.bit_count, + .is_constant = 1, + }, + }; + } + } + trap(); + } + case Node::Id::CONSTANT_INT: + return type; + default: + trap(); + } + } }; +[[nodiscard]] fn Node* add_constant_integer(Arena* arena, ConstantIntData data) +{ + auto* constant_int = Node::add(arena, { + .s = { + .type = + { + .id = Node::Type::Id::INTEGER, + .integer = + { + .constant = data.value, + .bit_count = data.bit_count, + .is_constant = 1, + }, + }, + .input_count = 1, + .id = Node::Id::CONSTANT_INT, + }, + .gvn = data.gvn, + .input_capacity = 1, + }); + constant_int->set_input(arena, data.input, 0); + return constant_int; +} + struct WorkList { using BitsetBackingType = u32; @@ -2254,34 +2465,25 @@ fn u64 parse_hex(String string) return value; } -struct ConstantIntData +fn u64 parse_decimal(String string) { - u64 value; - Node* input; - u32 gvn; - u8 bit_count; -}; + u64 value = 0; + for (u8 ch : string) + { + assert(((ch >= '0') & (ch <= '9'))); + value = (value * 10) + (ch - '0'); + } -[[nodiscard]] fn Node* add_constant_integer(Arena* arena, ConstantIntData data) -{ - auto* constant_int = Node::add(arena, { - .s = { - .type = { .id = NodeDataType::Id::INTEGER, .bit_count = data.bit_count, }, - .input_count = 1, - .id = Node::Id::CONSTANT_INT, - }, - .gvn = data.gvn, - .input_capacity = 1, - }); - constant_int->constant_int = data.value; - constant_int->set_input(data.input, 0); - return constant_int; + return value; } + + [[nodiscard]] fn Node* parse_constant_integer(Parser* parser, Arena* arena, String src, SemaType* type, u32 gvn, Node* input) { u64 value = 0; - auto starting_ch = src[parser->i]; + auto starting_index = parser->i; + auto starting_ch = src[starting_index]; if (starting_ch == '0') { @@ -2336,7 +2538,13 @@ struct ConstantIntData } else { - trap(); + while (is_decimal_digit(src[parser->i])) + { + parser->i += 1; + } + + auto slice = src.slice(starting_index, parser->i); + value = parse_decimal(slice); } Node* result = add_constant_integer(arena, { @@ -2345,6 +2553,7 @@ struct ConstantIntData .gvn = gvn, .bit_count = type->get_bit_count(), }); + return result; } @@ -2439,6 +2648,10 @@ struct ConstantIntData enum class CurrentOperation { NONE, + ADD, + ADD_ASSIGN, + SUB, + SUB_ASSIGN, }; u64 iterations = 0; @@ -2472,8 +2685,42 @@ struct ConstantIntData case CurrentOperation::NONE: previous_node = current_node; break; + case CurrentOperation::ADD: + case CurrentOperation::SUB: + { + Node::Id id; + switch (current_operation) + { + case CurrentOperation::NONE: + trap(); + case CurrentOperation::ADD: + id = Node::Id::INT_ADD; + break; + case CurrentOperation::SUB: + id = Node::Id::INT_SUB; + break; + case CurrentOperation::ADD_ASSIGN: + case CurrentOperation::SUB_ASSIGN: + trap(); + } + + auto* binary = Node::add_from_function(arena, analyzer->function, { + .type = current_node->type, + .input_count = 3, + .id = id, + }); + binary->set_input(arena, 0, 0); + binary->set_input(arena, previous_node, 1); + binary->set_input(arena, current_node, 2); + + previous_node = binary; + } break; + default: + trap(); } + previous_node = previous_node->peephole(arena, analyzer->function); + auto original_index = parser->i; u8 original = src[original_index]; @@ -2484,10 +2731,40 @@ struct ConstantIntData case parenthesis_close: case bracket_close: return previous_node; + case '+': + current_operation = CurrentOperation::ADD; + parser->i += 1; + + switch (src[parser->i]) + { + case '=': + current_operation = CurrentOperation::ADD_ASSIGN; + parser->i += 1; + break; + default: + break; + } + break; + case '-': + current_operation = CurrentOperation::SUB; + parser->i += 1; + + switch (src[parser->i]) + { + case '=': + current_operation = CurrentOperation::SUB_ASSIGN; + parser->i += 1; + break; + default: + break; + } + break; default: trap(); } + skip_space(parser, src); + iterations += 1; } } @@ -2520,12 +2797,12 @@ fn void analyze_local_block(Analyzer* analyzer, Parser* parser, Unit* unit, Aren Function* function = analyzer->function; Node* ret_node = Node::add_from_function(arena, function, { - .type = { .id = NodeDataType::Id::CONTROL }, + .type = { .id = Node::Type::Id::CONTROL }, .input_count = 2, .id = Node::Id::RETURN, }); - ret_node->set_input(function->root_node, 0); - ret_node->set_input(return_value, 1); + ret_node->set_input(arena, function->root_node, 0); + ret_node->set_input(arena, return_value, 1); } else { @@ -3022,7 +3299,7 @@ fn void analyze_function(Parser* parser, Thread* thread, Unit* unit, String src) }; function->root_node = Node::add_from_function_dynamic(thread->arena, function, { - .type = { .id = NodeDataType::Id::TUPLE }, + .type = { .id = Node::Type::Id::TUPLE }, .input_count = 2, .id = Node::Id::ROOT, }, 4); @@ -3030,7 +3307,7 @@ fn void analyze_function(Parser* parser, Thread* thread, Unit* unit, String src) // TODO: revisit // auto* control_node = root_node->project(unit, function, { - // .type = { .id = NodeDataType::Id::CONTROL }, + // .type = { .id = Node::Type::Id::CONTROL }, // }); // auto* memory_node = root_node->project(unit, function, {}); // auto* pointer_node = root_node->project(unit, function, {}); @@ -3048,7 +3325,7 @@ fn void analyze_function(Parser* parser, Thread* thread, Unit* unit, String src) // TODO: revisit // Node* ret_node = Node::add_from_function(unit, function); // ret_node->id = Node::Id::RETURN; - // ret_node->data_type = { .id = NodeDataType::Id::CONTROL }; + // ret_node->data_type = { .id = Node::Type::Id::CONTROL }; // ret_node->reallocate_edges(unit, 4); // ret_node->input_count = 2; // ret_node->set_input(unit, function, root_node, 0); @@ -3186,7 +3463,7 @@ global Instance instance; // continue; // } // -// if (node->data_type.id == NodeDataType::Id::MEMORY) +// if (node->data_type.id == Node::Type::Id::MEMORY) // { // trap(); // } @@ -3219,6 +3496,7 @@ global Instance instance; String test_file_paths[] = { strlit("tests/first/main.nat"), + strlit("tests/constant_prop/main.nat"), }; extern "C" void entry_point() diff --git a/tests/constant_prop/main.nat b/tests/constant_prop/main.nat new file mode 100644 index 0000000..f376aff --- /dev/null +++ b/tests/constant_prop/main.nat @@ -0,0 +1,4 @@ +fn[cc(.c)] main [export] () s32 +{ + return 2 + 4 - 1 - 5; +}