Commit b7826f0a authored by Vít Starý Novotný's avatar Vít Starý Novotný
Browse files

Added while and boolean expression tests for the LLVM compiler.

parent b3e4c743
Loading
Loading
Loading
Loading
+114 −86
Original line number Diff line number Diff line
@@ -48,13 +48,13 @@ size_t LLVMParser::llvm_store(size_t storage_offset, DataType type)
    assert(type != DT_ARBITRARY);
    size_t new_storage_offset = get_next_offset();
    if (type == DT_INTEGER)
        get_current_out() << "%" << new_storage_offset << " = alloca i32, align 4" << std::endl
                          << "store i32 %" << storage_offset << ", i32* %"
                          << new_storage_offset << ", align 4" << std::endl;
        get_current_out() << "%identifier." << new_storage_offset << " = alloca i32, align 4" << std::endl
                          << "store i32 %identifier." << storage_offset
                          << ", i32* %identifier." << new_storage_offset << ", align 4" << std::endl;
    else
        get_current_out() << "%" << new_storage_offset << " = alloca double, align 8" << std::endl
                          << "store double %" << storage_offset << ", double* %"
                          << new_storage_offset << ", align 8" << std::endl;
        get_current_out() << "%identifier." << new_storage_offset << " = alloca double, align 8" << std::endl
                          << "store double %identifier." << storage_offset
                          << ", double* %identifier." << new_storage_offset << ", align 8" << std::endl;
    return new_storage_offset;
}
size_t LLVMParser::llvm_load(size_t storage_offset, DataType type)
@@ -62,10 +62,12 @@ size_t LLVMParser::llvm_load(size_t storage_offset, DataType type)
    assert(type != DT_ARBITRARY);
    size_t new_storage_offset = get_next_offset();
    if (type == DT_INTEGER)
        get_current_out() << "%" << new_storage_offset << " = load i32, i32* %"
        get_current_out() << "%identifier." << new_storage_offset
                          << " = load i32, i32* %identifier."
                          << storage_offset << ", align 4" << std::endl;
    else
        get_current_out() << "%" << new_storage_offset << " = load double, double* %"
        get_current_out() << "%identifier." << new_storage_offset
                          << " = load double, double* %identifier."
                          << storage_offset << ", align 8" << std::endl;
    return new_storage_offset;
}
@@ -73,11 +75,27 @@ size_t LLVMParser::llvm_coerce_i32_to_double(size_t storage_offset)
{
    size_t loaded_storage_offset = llvm_load(storage_offset, DT_INTEGER);
    size_t offset = get_next_offset();
    get_current_out() << "%" << offset << " = sitofp i32 %"
    get_current_out() << "%identifier." << offset << " = sitofp i32 %identifier."
                      << loaded_storage_offset << " to double" << std::endl;
    return llvm_store(offset, DT_DOUBLE);
}

void LLVMParser::llvm_write_label(size_t label)
{
    get_current_out() << "label." << label << ":" << std::endl;
}
void LLVMParser::llvm_goto(size_t label)
{
    get_current_out() << "br label %label." << label << std::endl;
}
void LLVMParser::llvm_conditional_goto(
    size_t loaded_storage_offset, size_t label_false, size_t label_true)
{
    get_current_out() << "br i1 %identifier." << loaded_storage_offset
                      << ", label %label." << label_true
                      << ", label %label." << label_false << std::endl;
}

void LLVMParser::run()
{
    grammar_init();
@@ -87,7 +105,6 @@ void LLVMParser::grammar_init()
{
    symbols.push("");
    assert(symbols.insert("metadata::return_data_type", { ST_METADATA, { DT_ARBITRARY }, 0 }));
    assert(symbols.insert("metadata::current_offset", { ST_METADATA, { DT_ARBITRARY }, 1 }));
    get_current_out() << "declare i32 @printf(i8*, ...)" << std::endl
                      << "@.print_integer = private unnamed_addr constant [4 x i8] c\"%d\\0A\\00\", align 1" << std::endl
                      << "@.print_double = private unnamed_addr constant [5 x i8] c\"%lf\\0A\\00\", align 1" << std::endl;
@@ -104,11 +121,11 @@ void LLVMParser::grammar_block()
    grammar_variable_declaration_list();
    if (symbols.scope_name().empty())
        grammar_callable_declaration_list();
    std::string label_next = make_label();
    size_t label_next = get_next_label();
    if (symbols.scope_name().empty())
        get_current_out() << "define i32 @main() {" << std::endl;
    grammar_statement_list(label_next);
    write_label(label_next);
    llvm_write_label(label_next);
    if (symbols.scope_name().empty())
        get_current_out() << "ret i32 0" << std::endl
                          << "}" << std::endl;
@@ -153,13 +170,15 @@ void LLVMParser::grammar_variable_declaration()
        else
            get_current_out() << "double 0.000000e+00, align 8" << std::endl;
    } else {
        get_current_out() << "%" << name_token.term.payload << " = alloca ";
        get_current_out() << "%identifier." << name_token.term.payload << " = alloca ";
        if (type == DT_INTEGER)
            get_current_out() << "i32, align 4" << std::endl
                              << "store i32 0, i32* %" << name_token.term.payload << ", align 4" << std::endl;
                              << "store i32 0, i32* %identifier."
                              << name_token.term.payload << ", align 4" << std::endl;
        else
            get_current_out() << "double, align 8" << std::endl
                              << "store double 0.000000e+00, double* %" << name_token.term.payload << ", align 8" << std::endl;
                              << "store double 0.000000e+00, double* %identifier."
                              << name_token.term.payload << ", align 8" << std::endl;
    }
}

@@ -218,7 +237,6 @@ void LLVMParser::grammar_callable_declaration()
        throw expected_tokens(token, { Term::from_character.find(':')->second });
    DataType return_type = grammar_type();
    assert(symbols.insert("metadata::return_data_type", { ST_METADATA, { return_type }, 0 }));
    assert(symbols.insert("metadata::current_offset", { ST_METADATA, { DT_ARBITRARY }, 1 }));
    parameter_types.push_back(return_type);
    get_current_out() << "define ";
    if (return_type == DT_INTEGER)
@@ -284,10 +302,10 @@ void LLVMParser::grammar_parameter(std::vector<DataType>& parameter_types)
        get_current_out() << "i32";
    else
        get_current_out() << "double";
    get_current_out() << " %" << name_token.term.payload;
    get_current_out() << " %identifier." << name_token.term.payload;
}

void LLVMParser::grammar_statement_list(const std::string& label_next)
void LLVMParser::grammar_statement_list(size_t label_next)
{
    auto token = lexer.peekNext();
    auto eof = std::char_traits<char>::eof();
@@ -298,45 +316,52 @@ void LLVMParser::grammar_statement_list(const std::string& label_next)
            || !(token.term.payload == "end" || token.term.payload == "fi"
                   || token.term.payload == "else" || token.term.payload == "done"))
        && token.term.type != TT_EOF) {
        grammar_statement(label_next);
        grammar_statement();
        token = lexer.getNext();
        if (token.term.type != TT_SEMICOLON)
            throw expected_tokens(token, { Term::from_character.find(';')->second });
        grammar_statement_list(label_next);
    } else {
        get_current_out() << "br label %" << label_next << std::endl;
        llvm_goto(label_next);
    }
}

void LLVMParser::grammar_statement(const std::string& label_next)
void LLVMParser::grammar_statement()
{
    auto token = lexer.getNext();
    if (token.term.type != TT_ID)
        throw expected_tokens(token, { { TT_ID, "(arbitrary)" } });
    if (token.term.payload == "if") {
        std::string label_true = make_label();
        std::string label_false = make_label();
        size_t label_nested_next = get_next_label();
        size_t label_true = get_next_label();
        size_t label_false = get_next_label();
        grammar_bool_expression(label_true, label_false);
        token = lexer.getNext();
        if (token.term.type != TT_ID && token.term.payload != "then")
            throw expected_tokens(token, { { TT_ID, "then" } });
        write_label(label_true);
        grammar_statement_list(label_next);
        write_label(label_false);
        grammar_statement_if_tail(label_next);
        llvm_write_label(label_true);
        grammar_statement_list(label_nested_next);
        llvm_write_label(label_false);
        grammar_statement_if_tail(label_nested_next);
        llvm_goto(label_nested_next);
        llvm_write_label(label_nested_next);
    } else if (token.term.payload == "while") {
        std::string label_true = make_label();
        std::string label_loop = make_label();
        write_label(label_loop);
        grammar_bool_expression(label_true, label_next);
        size_t label_nested_next = get_next_label();
        size_t label_true = get_next_label();
        size_t label_loop = get_next_label();
        llvm_goto(label_loop);
        llvm_write_label(label_loop);
        grammar_bool_expression(label_true, label_nested_next);
        token = lexer.getNext();
        if (token.term.type != TT_ID && token.term.payload != "do")
            throw expected_tokens(token, { { TT_ID, "do" } });
        write_label(label_true);
        llvm_write_label(label_true);
        grammar_statement_list(label_loop);
        token = lexer.getNext();
        if (token.term.type != TT_ID && token.term.payload != "done")
            throw expected_tokens(token, { { TT_ID, "done" } });
        llvm_goto(label_nested_next);
        llvm_write_label(label_nested_next);
    } else if (token.term.payload == "return") {
        SymbolData& data = symbols.find("metadata::return_data_type").second;
        assert(data.data_type.size() == 1);
@@ -352,19 +377,20 @@ void LLVMParser::grammar_statement(const std::string& label_next)
                get_current_out() << "i32";
            else
                get_current_out() << "double";
            get_current_out() << " %" << loaded_storage_offset << std::endl;
            get_current_out() << " %identifier." << loaded_storage_offset << std::endl;
            get_next_offset();
        } else { // Print the returned value, when we are in the global scope.
            get_current_out() << "call i32 (i8*, ...) @printf(i8* getelementptr inbounds (";
            if (coerced_type == DT_INTEGER)
                get_current_out() << "[4 x i8], [4 x i8]* @.print_integer, i32 0, i32 0), i32 %";
                get_current_out() << "[4 x i8], [4 x i8]* @.print_integer, i32 0, i32 0), i32 %identifier.";
            else
                get_current_out() << "[5 x i8], [5 x i8]* @.print_double, i32 0, i32 0), double %";
            get_current_out() << loaded_storage_offset << ")" << std::endl
                              << "ret i32 0" << std::endl;
                get_current_out() << "[5 x i8], [5 x i8]* @.print_double, i32 0, i32 0), double %identifier.";
            get_current_out() << loaded_storage_offset << ")" << std::endl;
            get_next_offset();
            get_current_out() << "ret i32 0" << std::endl;
            get_next_offset();
        }
    } else {
    } else { // Variable assignment
        auto name_token = token;
        SymbolData data;
        bool is_identifier_global;
@@ -383,13 +409,14 @@ void LLVMParser::grammar_statement(const std::string& label_next)
        size_t loaded_storage_offset = llvm_load(storage_offset, coerced_type);
        get_current_out() << "store ";
        if (coerced_type == DT_INTEGER)
            get_current_out() << "i32 %" << loaded_storage_offset << ", i32* ";
            get_current_out() << "i32 %identifier." << loaded_storage_offset << ", i32* ";
        else
            get_current_out() << "double %" << loaded_storage_offset << ", double* ";
            get_current_out() << "double %identifier." << loaded_storage_offset
                              << ", double* ";
        if (is_identifier_global)
            get_current_out() << "@GLOBAL_" << name_token.term.payload;
        else
            get_current_out() << "%" << name_token.term.payload;
            get_current_out() << "%identifier." << name_token.term.payload;
        get_current_out() << ", align ";
        if (coerced_type == DT_INTEGER)
            get_current_out() << 4;
@@ -399,7 +426,7 @@ void LLVMParser::grammar_statement(const std::string& label_next)
    }
}

void LLVMParser::grammar_statement_if_tail(const std::string& label_next)
void LLVMParser::grammar_statement_if_tail(size_t label_next)
{
    auto token = lexer.getNext();
    if (token.term.type != TT_ID || !(token.term.payload == "fi" || token.term.payload == "else"))
@@ -446,7 +473,7 @@ DataType LLVMParser::grammar_arithmetic_expression(size_t& storage_offset)
            throw expected_tokens(token, { Term::from_character.find(')')->second });
        size_t loaded_storage_offset = get_next_offset();
        DataType return_type = data.data_type.back();
        get_current_out() << "%" << loaded_storage_offset << " = call ";
        get_current_out() << "%identifier." << loaded_storage_offset << " = call ";
        if (return_type == DT_INTEGER)
            get_current_out() << "i32";
        else
@@ -513,7 +540,7 @@ DataType LLVMParser::grammar_arithmetic_expression_tail(DataType left_operand_ty
        size_t loaded_right_operand_storage_offset = llvm_load(
            right_operand_storage_offset, coerced_type);
        size_t loaded_offset = get_next_offset();
        get_current_out() << "%" << loaded_offset << " = ";
        get_current_out() << "%identifier." << loaded_offset << " = ";
        if (coerced_type == DT_INTEGER)
            get_current_out() << "add";
        else
@@ -523,8 +550,8 @@ DataType LLVMParser::grammar_arithmetic_expression_tail(DataType left_operand_ty
            get_current_out() << "i32";
        else
            get_current_out() << "double";
        get_current_out() << " %" << loaded_left_operand_storage_offset
                          << ", %" << loaded_right_operand_storage_offset << std::endl;
        get_current_out() << " %identifier." << loaded_left_operand_storage_offset
                          << ", %identifier." << loaded_right_operand_storage_offset << std::endl;
        storage_offset = llvm_store(loaded_offset, coerced_type);
        return coerced_type;
    } else {
@@ -635,7 +662,7 @@ DataType LLVMParser::grammar_arithmetic_factor_tail(DataType left_operand_type,
        size_t loaded_right_operand_storage_offset = llvm_load(
            right_operand_storage_offset, coerced_type);
        size_t loaded_offset = get_next_offset();
        get_current_out() << "%" << loaded_offset << " = ";
        get_current_out() << "%identifier." << loaded_offset << " = ";
        if (token.term.type == TT_ARITHOP_MUL) {
            if (coerced_type == DT_INTEGER)
                get_current_out() << "mul";
@@ -658,8 +685,8 @@ DataType LLVMParser::grammar_arithmetic_factor_tail(DataType left_operand_type,
            get_current_out() << "i32";
        else
            get_current_out() << "double";
        get_current_out() << " %" << loaded_left_operand_storage_offset
                          << ", %" << loaded_right_operand_storage_offset << std::endl;
        get_current_out() << " %identifier." << loaded_left_operand_storage_offset
                          << ", %identifier." << loaded_right_operand_storage_offset << std::endl;
        storage_offset = llvm_store(loaded_offset, coerced_type);
        return coerced_type;
    } else {
@@ -691,10 +718,11 @@ DataType LLVMParser::grammar_arithmetic_operand(size_t& storage_offset)
        size_t loaded_nested_storage_offset = llvm_load(nested_storage_offset, type);
        size_t loaded_storage_offset = get_next_offset();
        if (type == DT_INTEGER)
            get_current_out() << "%" << loaded_storage_offset << " = sub i32 0, %"
                              << loaded_nested_storage_offset << std::endl;
            get_current_out() << "%identifier." << loaded_storage_offset
                              << " = sub i32 0, %identifier." << loaded_nested_storage_offset << std::endl;
        else
            get_current_out() << "%" << loaded_storage_offset << " = fsub double -0.000000e+00, %"
            get_current_out() << "%identifier." << loaded_storage_offset
                              << " = fsub double -0.000000e+00, %identifier."
                              << loaded_nested_storage_offset << std::endl;
        storage_offset = llvm_store(loaded_storage_offset, type);
        return type;
@@ -707,7 +735,7 @@ DataType LLVMParser::grammar_arithmetic_operand(size_t& storage_offset)
            throw undefined_variable(token);
        assert(data.data_type.size() == 1);
        DataType type = data.data_type.back();
        get_current_out() << "%" << loaded_storage_offset << " = load ";
        get_current_out() << "%identifier." << loaded_storage_offset << " = load ";
        if (type == DT_INTEGER)
            get_current_out() << "i32, i32*";
        else
@@ -716,21 +744,23 @@ DataType LLVMParser::grammar_arithmetic_operand(size_t& storage_offset)
        if (is_identifier_global)
            get_current_out() << "@GLOBAL_";
        else
            get_current_out() << "%";
            get_current_out() << "%identifier.";
        get_current_out() << token.term.payload << ", align 4" << std::endl;
        storage_offset = llvm_store(loaded_storage_offset, type);
        return type;
    } else if (token.term.type == TT_INTEGER) {
        storage_offset = get_next_offset();
        get_current_out() << "%" << storage_offset << " = alloca i32, align 4" << std::endl
                          << "store i32 " << token.term.payload << ", i32* %"
                          << storage_offset << ", align 4" << std::endl;
        get_current_out() << "%identifier." << storage_offset
                          << " = alloca i32, align 4" << std::endl
                          << "store i32 " << token.term.payload
                          << ", i32* %identifier." << storage_offset << ", align 4" << std::endl;
        return DT_INTEGER;
    } else if (token.term.type == TT_REAL) {
        storage_offset = get_next_offset();
        get_current_out() << "%" << storage_offset << " = alloca double, align 8" << std::endl
                          << "store double " << token.term.payload << ", double* %"
                          << storage_offset << ", align 8" << std::endl;
        get_current_out() << "%identifier." << storage_offset
                          << " = alloca double, align 8" << std::endl
                          << "store double " << token.term.payload
                          << ", double* %identifier." << storage_offset << ", align 8" << std::endl;
        return DT_DOUBLE;
    }
    assert(false); // Should never reach here.
@@ -738,16 +768,16 @@ DataType LLVMParser::grammar_arithmetic_operand(size_t& storage_offset)
}

void LLVMParser::grammar_bool_expression(
    const std::string& label_true, const std::string& label_false)
    size_t label_true, size_t label_false)
{
    std::string label_nested_false = make_label();
    size_t label_nested_false = get_next_label();
    grammar_bool_factor(label_true, label_nested_false);
    grammar_bool_expression_tail(label_true, label_false, label_nested_false);
}

void LLVMParser::grammar_bool_expression_tail(
    const std::string& label_true,
    const std::string& label_false, const std::string& label_nested_false)
    size_t label_true,
    size_t label_false, size_t label_nested_false)
{
    auto token = lexer.peekNext();
    if ((token.term.type != TT_ID
@@ -759,25 +789,25 @@ void LLVMParser::grammar_bool_expression_tail(
                                         Term::from_character.find(']')->second });
    if (token.term.type == TT_ID && token.term.payload == "or") {
        lexer.getNext();
        write_label(label_nested_false);
        llvm_write_label(label_nested_false);
        grammar_bool_expression(label_true, label_false);
    } else {
        write_label(label_nested_false);
        get_current_out() << "br label %" << label_false << std::endl;
        llvm_write_label(label_nested_false);
        llvm_goto(label_false);
    }
}

void LLVMParser::grammar_bool_factor(
    const std::string& label_true, const std::string& label_false)
    size_t label_true, size_t label_false)
{
    std::string label_nested_true = make_label();
    size_t label_nested_true = get_next_label();
    grammar_bool_operand(label_nested_true, label_false);
    grammar_bool_factor_tail(label_true, label_nested_true, label_false);
}

void LLVMParser::grammar_bool_factor_tail(
    const std::string& label_true, const std::string& label_nested_true,
    const std::string& label_false)
    size_t label_true, size_t label_nested_true,
    size_t label_false)
{
    auto token = lexer.peekNext();
    if ((token.term.type != TT_ID
@@ -789,16 +819,16 @@ void LLVMParser::grammar_bool_factor_tail(
                                         Term::from_character.find(']')->second });
    if (token.term.type == TT_ID && token.term.payload == "and") {
        lexer.getNext();
        write_label(label_nested_true);
        llvm_write_label(label_nested_true);
        grammar_bool_factor(label_true, label_false);
    } else {
        write_label(label_nested_true);
        get_current_out() << "br label %" << label_true << std::endl;
        llvm_write_label(label_nested_true);
        llvm_goto(label_true);
    }
}

void LLVMParser::grammar_bool_operand(
    const std::string& label_true, const std::string& label_false)
    size_t label_true, size_t label_false)
{
    auto token = lexer.peekNext();
    if (token.term.type != TT_ID && token.term.type != TT_INTEGER
@@ -822,14 +852,12 @@ void LLVMParser::grammar_bool_operand(
        && (token.term.payload == "true" || token.term.payload == "false")) {
        lexer.getNext();
        if (token.term.payload == "true")
            get_current_out() << "br label %" << label_true << std::endl;
            llvm_goto(label_true);
        else
            get_current_out() << "br label %" << label_false << std::endl;
            llvm_goto(label_false);
    } else {
        size_t loaded_storage_offset = grammar_relation_expression();
        get_current_out() << "br i1 %" << loaded_storage_offset
                          << ", label %" << label_true
                          << ", label %" << label_false << std::endl;
        llvm_conditional_goto(loaded_storage_offset, label_false, label_true);
    }
}

@@ -851,8 +879,6 @@ size_t LLVMParser::grammar_relation_expression_tail(
                                         Term::from_character.find('>')->second });
    size_t right_operand_storage_offset;
    DataType right_operand_type = grammar_arithmetic_expression(right_operand_storage_offset);
    size_t loaded_storage_offset = get_next_offset();
    get_current_out() << "%" << loaded_storage_offset << " = ";
    DataType coerced_type = arithmetic_binary_operator_coerce(
        left_operand_type, right_operand_type,
        left_operand_storage_offset, right_operand_storage_offset);
@@ -860,6 +886,8 @@ size_t LLVMParser::grammar_relation_expression_tail(
        left_operand_storage_offset, left_operand_type);
    size_t loaded_right_operand_storage_offset = llvm_load(
        right_operand_storage_offset, right_operand_type);
    size_t loaded_storage_offset = get_next_offset();
    get_current_out() << "%identifier." << loaded_storage_offset << " = ";
    if (coerced_type == DT_INTEGER) {
        get_current_out() << "icmp ";
        switch (token.term.type) {
@@ -875,8 +903,8 @@ size_t LLVMParser::grammar_relation_expression_tail(
        default:
            assert(false); // Should never reach here.
        }
        get_current_out() << " i32 %" << loaded_left_operand_storage_offset
                          << ", %" << loaded_right_operand_storage_offset << std::endl;
        get_current_out() << " i32 %identifier." << loaded_left_operand_storage_offset
                          << ", %identifier." << loaded_right_operand_storage_offset << std::endl;
    } else {
        get_current_out() << "fcmp ";
        switch (token.term.type) {
@@ -892,8 +920,8 @@ size_t LLVMParser::grammar_relation_expression_tail(
        default:
            assert(false); // Should never reach here.
        }
        get_current_out() << " double %" << loaded_left_operand_storage_offset
                          << ", %" << loaded_right_operand_storage_offset << std::endl;
        get_current_out() << " double %identifier." << loaded_left_operand_storage_offset
                          << ", %identifier." << loaded_right_operand_storage_offset << std::endl;
    }
    return loaded_storage_offset;
}
+20 −22

File changed.

Preview size limit exceeded, changes collapsed.

+33 −2

File changed.

Preview size limit exceeded, changes collapsed.