diff --git a/bootstrap/main.cpp b/bootstrap/main.cpp index 3c20e8e..8bd53d8 100644 --- a/bootstrap/main.cpp +++ b/bootstrap/main.cpp @@ -1209,10 +1209,12 @@ struct PinnedArray { if (index >= 0 & index < length) { + auto original_len = length; T item = pointer[index]; T last = pointer[length - 1]; pointer[index] = last; pop(); + assert(length == original_len - 1); return item; } @@ -1533,6 +1535,7 @@ struct NodeType return (payload.constant.is_constant == other.payload.constant.is_constant) & (payload.constant.constant == other.payload.constant.constant); case NodeType::Id::LIVE_CONTROL: case NodeType::Id::DEAD_CONTROL: + case NodeType::Id::BOTTOM: return 1; default: trap(); @@ -1967,6 +1970,7 @@ struct Node SYMBOL_FUNCTION, CALL, REGION, + REGION_LOOP, PHI, INTEGER_ADD, @@ -2193,9 +2197,28 @@ struct Node } case Id::ROOT: case Id::IF: - case Id::INTEGER_ADD: return 0; + case Id::INTEGER_ADD: + { + auto* left = inputs[1]; + auto* right = inputs[2]; + assert(!(left->type.is_constant() && right->type.is_constant())); + + if (right->type.id == NodeType::Id::INTEGER && right->type.payload.constant.constant == 0) + { + return left; + } + + if (left == right) + { + trap(); + } + + return 0; + } + case Id::REGION_LOOP: case Id::REGION: + if (!region_in_progress()) { // Find dead input for (u32 i = 1; i < inputs.length; i += 1) @@ -2205,9 +2228,9 @@ struct Node trap(); } } - - return 0; } + + return 0; case Id::SCOPE: trap(); // TODO: @@ -2230,21 +2253,51 @@ struct Node } case Id::PHI: { - if (phi_same_inputs()) + auto* region = phi_get_region(); + auto is_r = region->is_region(); + if (!is_r || region->region_in_progress()) { - return inputs[1]; + return 0; } else { + // Single unique input search + Node* live = 0; + Node* region = phi_get_region(); + for (u32 i = 1; i < inputs.length; i += 1) + { + + if (region->inputs[i]->type.id != NodeType::Id::DEAD_CONTROL && inputs[i] != this) + { + if (!live || live == inputs[i]) + { + live = inputs[i]; + } + else + { + live = 0; + break; + } + } + } + + if (live) + { + return live; + } + Node* operand = inputs[1]; if (operand->inputs.length == 3 && !operand->inputs[0] && !operand->is_cfg() && phi_same_operand()) { - auto lefts = thread->arena->allocate_slice(inputs.length); - auto rights = thread->arena->allocate_slice(inputs.length); - lefts[0] = rights[0] = inputs[0]; + u32 input_count = inputs.length; + auto lefts = thread->arena->allocate_slice(input_count); + auto rights = thread->arena->allocate_slice(input_count); - for (u32 i = 1; i < inputs.length; i += 1) + lefts[0] = inputs[0]; + rights[0] = inputs[0]; + + for (u32 i = 1; i < input_count; i += 1) { lefts[i] = inputs[i]->inputs[1]; rights[i] = inputs[i]->inputs[2]; @@ -2265,12 +2318,12 @@ struct Node }); right_phi->payload.phi.label = payload.phi.label; right_phi = right_phi->peephole(thread, function); - return operand->copy(thread, left_phi, right_phi); - } - else - { - return 0; + + auto* result = operand->copy(thread, left_phi, right_phi); + return result; } + + return 0; } } case Id::RETURN: @@ -2320,10 +2373,11 @@ struct Node case Id::STOP: case Id::RETURN: case Id::REGION: + case Id::REGION_LOOP: case Id::IF: return 1; case Id::PROJECTION: - trap(); + return (payload.projection.index == 0) || (get_control()->id == Node::Id::IF); default: return 0; } @@ -2411,7 +2465,7 @@ struct Node auto* constant_int = Node::add(thread, { .type = type, .inputs = { .pointer = &function->root_node, .length = 1 }, - .id = Node::Id::CONSTANT_INT, + .id = Id::CONSTANT_INT, }); auto* result = constant_int->peephole(thread, function); return dead_code_elimination(thread->arena, result); @@ -2446,6 +2500,7 @@ struct Node default: return 0; case Id::CONSTANT_INT: + case Id::CONSTANT_CONTROL: return 1; } } @@ -2593,7 +2648,13 @@ struct Node }, }; } + case Node::Id::REGION_LOOP: case Node::Id::REGION: + if (region_in_progress()) + { + return { .id = Type::Id::LIVE_CONTROL }; + } + else { Type ty = { .id = Type::Id::DEAD_CONTROL }; for (u32 i = 1; i < inputs.length; i += 1) @@ -2604,12 +2665,41 @@ struct Node return ty; } case Node::Id::PHI: - return { .id = Type::Id::BOTTOM }; + { + auto* region = phi_get_region(); + auto is_r = region->is_region(); + if (!is_r || region->region_in_progress()) + { + return { .id = Type::Id::BOTTOM }; + } + else + { + Node::Type ty = { .id = Type::Id::TOP }; + + for (u32 i = 1; i < inputs.length; i += 1) + { + ty = ty.meet(inputs[i]->type); + } + + return ty; + } + } default: trap(); } } + u8 is_region() + { + switch (id) + { + case Id::REGION: case Id::REGION_LOOP: + return 1; + default: + return 0; + } + } + method u8 is_associative() { switch (id) @@ -2622,6 +2712,7 @@ struct Node } method Node* associative_phi_constant(u8 should_rotate) { + unused(should_rotate); assert(is_associative()); trap(); } @@ -2658,13 +2749,15 @@ struct Node case NodeType::Id::INVALID: trap(); case NodeType::Id::BOTTOM: - trap(); + // TODO: + return unit->get_integer_type(32, 1); case NodeType::Id::TOP: trap(); case NodeType::Id::LIVE_CONTROL: case NodeType::Id::DEAD_CONTROL: trap(); case NodeType::Id::INTEGER: + // TODO: return unit->get_integer_type(32, 1); case NodeType::Id::MULTIVALUE: trap(); @@ -2703,7 +2796,17 @@ struct Node method u8 all_constants() { - for (u32 i = 0; i < inputs.length; i += 1) + if (id == Id::PHI) + { + auto* region = phi_get_region(); + auto is_r = region->is_region(); + if (!is_r || region->region_in_progress()) + { + return 0; + } + } + + for (u32 i = 1; i < inputs.length; i += 1) { if (!inputs[i]->type.is_constant()) { @@ -2720,6 +2823,8 @@ struct Node { case Id::ROOT: return 0; + case Id::REGION_LOOP: + return loop_entry(); case Id::REGION: if (payload.region.immediate_dominator) { @@ -2786,6 +2891,82 @@ struct Node } } } + + method u8 region_in_progress() + { + assert(is_region()); + return !(inputs[inputs.length - 1]); + } + + method Node* loop_entry() + { + assert(id == Id::REGION_LOOP); + return inputs[1]; + } + + method Node* loop_backedge() + { + assert(id == Id::REGION_LOOP); + return inputs[2]; + } + + method Node* set_control(Arena* arena, Node* node) + { + assert(id == Id::SCOPE); + + return set_input(arena, 0, node); + } + + method Node* phi_get_region() + { + assert(id == Id::PHI); + return inputs[0]; + } + + method void scope_end_loop(Thread* thread, Function* function, Node* back, Node* exit) + { + assert(id == Id::SCOPE); + assert(back->id == Id::SCOPE); + assert(exit->id == Id::SCOPE); + + Node* control_node = get_control(); + assert(control_node->id == Id::REGION_LOOP); + assert(control_node->region_in_progress()); + control_node->set_input(thread->arena, 2, back->get_control()); + + for (u32 i = 1; i < inputs.length; i += 1) + { + auto* phi_node = inputs[i]; + assert(phi_node->id == Id::PHI); + assert(phi_node->phi_get_region() == control_node); + assert(!(phi_node->inputs[2])); + phi_node->set_input(thread->arena, 2, back->inputs[2]); + Node* input = phi_node->peephole(thread, function); + + if (input != phi_node) + { + phi_node->subsume(thread->arena, input); + } + } + + back->kill(thread->arena); + } + + method void subsume(Arena* arena, Node* node) + { + assert(node != this); + + while (outputs.length > 0) + { + Node* n = outputs.pop(); + s32 index = n->inputs.slice().find_index(this); + assert(index != -1); + n->inputs[index] = node; + node->add_output(n); + } + + kill(arena); + } }; static_assert(sizeof(Node) == 128); @@ -3340,7 +3521,7 @@ struct Analyzer method Node* set_control(Arena* arena, Node* node) { - return scope->set_input(arena, 0, node); + return scope->set_control(arena, node); } method void kill_control(Arena* arena) @@ -3376,13 +3557,12 @@ struct Analyzer return scope->get_control(); } - method Node* duplicate_scope(Thread* thread) + method Node* duplicate_scope(Thread* thread, u8 loop) { + auto original_input_count = scope->inputs.length; auto* duplicate = create_scope(thread); - - duplicate->payload.scope.stack.ensure_capacity(scope->payload.scope.stack.capacity); - - // TODO: make this more efficient + + // // TODO: make this more efficient for (auto& hashmap: scope->payload.scope.stack.slice()) { Hashmap duplicate_hashmap = {}; @@ -3397,14 +3577,36 @@ struct Analyzer duplicate->payload.scope.stack.append_one(duplicate_hashmap); } - + duplicate->add_input(get_control()); - + for (u32 i = 1; i < scope->inputs.length; i += 1) { - duplicate->add_input(scope->inputs[i]); + if (loop) + { + auto names = scope_reverse_names(thread->arena, scope); + Node* inputs[] = { + get_control(), + scope->inputs[i], + 0, + }; + String label = names[i]; + auto* phi_node = Node::add(thread, { + .type = {}, + .inputs = array_to_slice(inputs), + .id = Node::Id::PHI, + }); + phi_node->payload.phi.label = label; + phi_node = phi_node->peephole(thread, function); + duplicate->add_input(phi_node); + scope->set_input(thread->arena, i, duplicate->inputs[i]); + } + else + { + duplicate->add_input(scope->inputs[i]); + } } - + assert(duplicate->inputs.length == original_input_count); return duplicate; } @@ -3423,7 +3625,7 @@ struct Analyzer .type = {}, .inputs = array_to_slice(inputs), .id = Node::Id::REGION, - })->peephole(thread, function)); + })->keep()); auto names = scope_reverse_names(thread->arena, scope_a); // Skip input[0] ($ctrl) @@ -3453,7 +3655,7 @@ struct Analyzer } scope_b->kill(thread->arena); - return region_node; + return region_node->unkeep()->peephole(thread, function); } }; @@ -3873,6 +4075,10 @@ fn Node* scope_lookup(Analyzer* analyzer, Arena* arena, String name) INTEGER_COMPARE_LESS_EQUAL, INTEGER_COMPARE_GREATER, INTEGER_COMPARE_GREATER_EQUAL, + INTEGER_SHIFT_LEFT, + INTEGER_SHIFT_LEFT_ASSIGN, + INTEGER_SHIFT_RIGHT, + INTEGER_SHIFT_RIGHT_ASSIGN, }; u64 iterations = 0; @@ -3931,6 +4137,10 @@ fn Node* scope_lookup(Analyzer* analyzer, Arena* arena, String name) case CurrentOperation::INTEGER_COMPARE_LESS_EQUAL: case CurrentOperation::INTEGER_COMPARE_GREATER: case CurrentOperation::INTEGER_COMPARE_GREATER_EQUAL: + case CurrentOperation::INTEGER_SHIFT_LEFT: + case CurrentOperation::INTEGER_SHIFT_LEFT_ASSIGN: + case CurrentOperation::INTEGER_SHIFT_RIGHT: + case CurrentOperation::INTEGER_SHIFT_RIGHT_ASSIGN: trap(); } @@ -3995,10 +4205,14 @@ fn Node* scope_lookup(Analyzer* analyzer, Arena* arena, String name) previous_node = binary; } break; case CurrentOperation::ASSIGN: + case CurrentOperation::INTEGER_SHIFT_LEFT: + case CurrentOperation::INTEGER_SHIFT_LEFT_ASSIGN: + case CurrentOperation::INTEGER_SHIFT_RIGHT: + case CurrentOperation::INTEGER_SHIFT_RIGHT_ASSIGN: case CurrentOperation::INTEGER_ADD_ASSIGN: case CurrentOperation::INTEGER_SUB_ASSIGN: trap(); - } + } previous_node = previous_node->peephole(thread, analyzer->function); @@ -4054,6 +4268,64 @@ fn Node* scope_lookup(Analyzer* analyzer, Arena* arena, String name) break; } break; + case '<': + current_operation = CurrentOperation::INTEGER_COMPARE_LESS; + parser->i += 1; + + switch (src[parser->i]) + { + case '=': + current_operation = CurrentOperation::INTEGER_COMPARE_LESS_EQUAL; + parser->i += 1; + break; + case '<': // Shift left + current_operation = CurrentOperation::INTEGER_SHIFT_LEFT; + parser->i += 1; + + switch (src[parser->i]) + { + case '=': + current_operation = CurrentOperation::INTEGER_SHIFT_LEFT_ASSIGN; + parser->i += 1; + break; + default: + break; + } + + break; + default: + break; + } + break; + case '>': + current_operation = CurrentOperation::INTEGER_COMPARE_GREATER; + parser->i += 1; + + switch (src[parser->i]) + { + case '=': + current_operation = CurrentOperation::INTEGER_COMPARE_GREATER_EQUAL; + parser->i += 1; + break; + case '>': // Shift right + current_operation = CurrentOperation::INTEGER_SHIFT_RIGHT; + parser->i += 1; + + switch (src[parser->i]) + { + case '=': + current_operation = CurrentOperation::INTEGER_SHIFT_RIGHT_ASSIGN; + parser->i += 1; + break; + default: + break; + } + + break; + default: + break; + } + break; case function_argument_start: { assert(previous_node->id == Node::Id::SYMBOL_FUNCTION); @@ -4151,7 +4423,7 @@ fn Node* analyze_statement(Analyzer* analyzer, Parser* parser, Unit* unit, Threa Node* if_false = if_node->project(thread, if_node, 1, strlit("False"))->peephole(thread, function); u32 original_input_count = analyzer->scope->inputs.length; - auto* false_scope = analyzer->duplicate_scope(thread); + auto* false_scope = analyzer->duplicate_scope(thread, 0); analyzer->set_control(thread->arena, if_true); assert(analyzer->scope->get_control()); @@ -4175,6 +4447,8 @@ fn Node* analyze_statement(Analyzer* analyzer, Parser* parser, Unit* unit, Threa parser->skip_space(src); analyze_statement(analyzer, parser, unit, thread, src); + + false_scope = analyzer->scope; } else { @@ -4193,6 +4467,67 @@ fn Node* analyze_statement(Analyzer* analyzer, Parser* parser, Unit* unit, Threa statement_node = analyzer->set_control(thread->arena, merged_scope); assert(statement_node); } + else if (identifier.equal(strlit("while"))) + { + parser->skip_space(src); + + parser->expect_character(src, parenthesis_open); + + Node* loop_inputs[] = { + 0, + analyzer->get_control(), + 0, + }; + auto* loop_node = Node::add(thread, { + .type = {}, + .inputs = array_to_slice(loop_inputs), + .id = Node::Id::REGION_LOOP, + })->peephole(thread, function); + + analyzer->set_control(thread->arena, loop_node); + + Node* head = analyzer->scope->keep(); + auto is_loop = 1; + analyzer->scope = analyzer->duplicate_scope(thread, is_loop); + + parser->skip_space(src); + + auto* predicate_node = analyze_expression(analyzer, parser, unit, thread, src, 0, Side::right); + + parser->skip_space(src); + + parser->expect_character(src, parenthesis_close); + + parser->skip_space(src); + Node* if_inputs[] = { + analyzer->get_control(), + predicate_node, + }; + + auto* if_node = Node::add(thread, { + .type = {}, + .inputs = array_to_slice(if_inputs), + .id = Node::Id::IF, + })->keep()->peephole(thread, function); + + Node* if_true = if_node->project(thread, if_node, 0, strlit("True"))->peephole(thread, function); + if_node->unkeep(); + Node* if_false = if_node->project(thread, if_node, 1, strlit("False"))->peephole(thread, function); + + auto* exit_scope = analyzer->duplicate_scope(thread, 0); + exit_scope->set_control(thread->arena, if_false); + + analyzer->set_control(thread->arena, if_true); + + analyze_statement(analyzer, parser, unit, thread, src); + + head->scope_end_loop(thread, function, analyzer->scope, exit_scope); + head->unkeep()->kill(thread->arena); + + analyzer->scope = exit_scope; + statement_node = exit_scope; + assert(statement_node); + } if (statement_node) { @@ -5068,6 +5403,7 @@ global String test_file_paths[] = { strlit("tests/function_call_args/main.nat"), strlit("tests/comparison/main.nat"), strlit("tests/if/main.nat"), + strlit("tests/while/main.nat"), }; #ifdef __linux__ diff --git a/tests/while/main.nat b/tests/while/main.nat new file mode 100644 index 0000000..053225f --- /dev/null +++ b/tests/while/main.nat @@ -0,0 +1,136 @@ +fn while0(arg: s32) s32 +{ + >a: s32 = arg; + while (a < 10) + { + a = a + 1; + } + + return a; +} + +fn while1(arg: s32) s32 +{ + >a: s32 = 1; + if (arg) + { + } + else + { + while (a < 10) + { + a = a + 1; + } + } + + return a; +} + +fn while2(arg: s32) s32 +{ + >sum: s32 = 0; + >i: s32 = 0; + + while (i < arg) + { + i = i + 1; + >j: s32 = 0; + while (j < arg) + { + sum = sum + j; + j = j + 1; + } + } + + return sum; +} + +fn while3(arg: s32) s32 +{ + >a: s32 = 1; + >b: s32 = 2; + + while (a < 10) + { + if (a == 2) + { + a = 3; + } + else + { + b = 4; + } + } + + return b; +} + +fn while4(arg: s32) s32 +{ + >a: s32 = 1; + >b: s32 = 2; + + while (a < 10) + { + if (a == 2) + { + a = 3; + } + else + { + b = 4; + } + + b = b + 1; + a = a + 1; + } + + return b; +} + +fn while5(arg: s32) s32 +{ + >a: s32 = 1; + while (a < 10) + { + a = a + 1; + a = a + 2; + } + + return a; +} + +fn while6(arg: s32) s32 +{ + >a: s32 = 1; + while (arg) + { + a = 2; + } + + return a; +} + +fn while7(arg: s32) s32 +{ + >a: s32 = 1; + while (a < 10) + { + >b: s32 = a + 1; + a = b + 2; + } + + return a; +} + +fn[cc(.c)] main[export]() s32 +{ + return while0(0) + + while1(1) + + while2(2) + + while3(3) + + while4(4) + + while5(5) + + while6(6) + + while7(7); +}