<commit_before>#include <unordered_map>
#include <vector>
#include <cassert>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/Verifier.h>
#include "irgen.h"
#include "../sema/typecheck.h"
#include "../parser/parser.hpp"

namespace {

llvm::LLVMContext ctx;
llvm::IRBuilder<> builder(ctx);
llvm::Module module("", ctx);
std::unordered_map<std::string, llvm::Value*> globalValues;
std::unordered_map<std::string, llvm::Value*> localValues;
std::unordered_map<std::string, llvm::Function*> funcs;
std::unordered_map<std::string, std::pair<llvm::StructType*, const TypeDecl*>> structs;
const std::vector<Decl>* globalDecls;
const Decl* currentDecl;

void setLocalValue(std::string name, llvm::Value* value) {
    bool wasInserted = localValues.emplace(std::move(name), value).second;
    assert(wasInserted);
}

void codegen(const Decl& decl);

llvm::Value* findValue(llvm::StringRef name) {
    auto it = localValues.find(name);
    if (it == localValues.end()) {
        it = globalValues.find(name);

        // FIXME: It would probably be better to not access the symbol table here.
        if (it == globalValues.end()) {
            codegen(findInSymbolTable(name, SrcLoc::invalid()));
            it = globalValues.find(name);
            assert(it != globalValues.end());
        }
    }
    return it->second;
}

template<typename From, typename To>
std::vector<To> map(const std::vector<From>& from, To (&func)(const From&)) {
    std::vector<To> to;
    to.reserve(from.size());
    for (const auto& e : from) to.emplace_back(func(e));
    return to;
}

void codegen(const TypeDecl& decl);

llvm::Type* toIR(const Type& type) {
    switch (type.getKind()) {
        case TypeKind::BasicType: {
            const auto& name = type.getBasicType().name;
            if (name == "void") return llvm::Type::getVoidTy(ctx);
            if (name == "bool") return llvm::Type::getInt1Ty(ctx);
            if (name == "char") return llvm::Type::getInt8Ty(ctx);
            if (name == "int" || name == "uint") return llvm::Type::getInt32Ty(ctx);
            if (name == "int8" || name == "uint8") return llvm::Type::getInt8Ty(ctx);
            if (name == "int16" || name == "uint16") return llvm::Type::getInt16Ty(ctx);
            if (name == "int32" || name == "uint32") return llvm::Type::getInt32Ty(ctx);
            if (name == "int64" || name == "uint64") return llvm::Type::getInt64Ty(ctx);
            auto it = structs.find(name);
            if (it == structs.end()) {
                // Custom type that has not been declared yet, search for it in the symbol table.
                codegen(findInSymbolTable(name, SrcLoc::invalid()).getTypeDecl());
                it = structs.find(name);
            }
            return it->second.first;
        }
        case TypeKind::ArrayType: {
            const auto& array = type.getArrayType();
            return llvm::ArrayType::get(toIR(*array.elementType), array.size);
        }
        case TypeKind::TupleType:
            assert(false && "IRGen doesn't support tuple types yet");
        case TypeKind::FuncType:
            assert(false && "IRGen doesn't support function types yet");
        case TypeKind::PtrType: {
            auto* pointeeType = toIR(*type.getPtrType().pointeeType);
            if (!pointeeType->isVoidTy()) return llvm::PointerType::get(pointeeType, 0);
            else return llvm::PointerType::get(llvm::Type::getInt8Ty(ctx), 0);
        }
    }
}

llvm::Value* codegen(const Expr& expr);
llvm::Value* codegenLvalue(const Expr& expr);

llvm::Value* codegen(const VariableExpr& expr) {
    auto* value = findValue(expr.identifier);
    if (auto* arg = llvm::dyn_cast<llvm::Argument>(value)) return arg;
    return builder.CreateLoad(value, expr.identifier);
}

llvm::Value* codegenLvalue(const VariableExpr& expr) {
    return findValue(expr.identifier);
}

llvm::Value* codegen(const StrLiteralExpr& expr) {
    return builder.CreateGlobalStringPtr(expr.value);
}

llvm::Value* codegen(const IntLiteralExpr& expr, const Expr& parent) {
    return llvm::ConstantInt::getSigned(toIR(parent.getType()), expr.value);
}

llvm::Value* codegen(const BoolLiteralExpr& expr) {
    return expr.value ? llvm::ConstantInt::getTrue(ctx) : llvm::ConstantInt::getFalse(ctx);
}

llvm::Value* codegen(const NullLiteralExpr& expr, const Expr& parent) {
    return llvm::ConstantPointerNull::get(llvm::cast<llvm::PointerType>(toIR(parent.getType())));
}

using CreateNegFunc       = decltype(&llvm::IRBuilder<>::CreateNeg);
using CreateICmpFunc      = decltype(&llvm::IRBuilder<>::CreateICmpEQ);
using CreateAddSubMulFunc = decltype(&llvm::IRBuilder<>::CreateAdd);
using CreateDivFunc       = decltype(&llvm::IRBuilder<>::CreateSDiv);

llvm::Value* codegenPrefixOp(const PrefixExpr& expr, CreateNegFunc intFunc) {
    return (builder.*intFunc)(codegen(*expr.operand), "", false, false);
}

llvm::Value* codegen(const PrefixExpr& expr) {
    switch (expr.op.rawValue) {
        case PLUS:  return codegen(*expr.operand);
        case MINUS: return codegenPrefixOp(expr, &llvm::IRBuilder<>::CreateNeg);
        case STAR:  return builder.CreateLoad(codegen(*expr.operand));
        case AND:   return codegenLvalue(*expr.operand);
        default:    assert(false);
    }
}

llvm::Value* codegenLvalue(const PrefixExpr& expr) {
    switch (expr.op.rawValue) {
        case STAR:  return codegen(*expr.operand);
        default:    assert(false);
    }
}

llvm::Value* codegenBinaryOp(const BinaryExpr& expr, CreateICmpFunc intFunc) {
    return (builder.*intFunc)(codegen(*expr.left), codegen(*expr.right), "");
}

llvm::Value* codegenBinaryOp(const BinaryExpr& expr, CreateAddSubMulFunc intFunc) {
    return (builder.*intFunc)(codegen(*expr.left), codegen(*expr.right), "", false, false);
}

llvm::Value* codegenBinaryOp(const BinaryExpr& expr, CreateDivFunc intFunc) {
    return (builder.*intFunc)(codegen(*expr.left), codegen(*expr.right), "", false);
}

llvm::Value* codegen(const BinaryExpr& expr) {
    assert(expr.left->getType().isImplicitlyConvertibleTo(expr.right->getType())
        || expr.right->getType().isImplicitlyConvertibleTo(expr.left->getType()));

    switch (expr.op.rawValue) {<commit_msg>[irgen] Fix argument evaluation order in binary op codegen<commit_after>#include <unordered_map>
#include <vector>
#include <cassert>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/Verifier.h>
#include "irgen.h"
#include "../sema/typecheck.h"
#include "../parser/parser.hpp"

namespace {

llvm::LLVMContext ctx;
llvm::IRBuilder<> builder(ctx);
llvm::Module module("", ctx);
std::unordered_map<std::string, llvm::Value*> globalValues;
std::unordered_map<std::string, llvm::Value*> localValues;
std::unordered_map<std::string, llvm::Function*> funcs;
std::unordered_map<std::string, std::pair<llvm::StructType*, const TypeDecl*>> structs;
const std::vector<Decl>* globalDecls;
const Decl* currentDecl;

void setLocalValue(std::string name, llvm::Value* value) {
    bool wasInserted = localValues.emplace(std::move(name), value).second;
    assert(wasInserted);
}

void codegen(const Decl& decl);

llvm::Value* findValue(llvm::StringRef name) {
    auto it = localValues.find(name);
    if (it == localValues.end()) {
        it = globalValues.find(name);

        // FIXME: It would probably be better to not access the symbol table here.
        if (it == globalValues.end()) {
            codegen(findInSymbolTable(name, SrcLoc::invalid()));
            it = globalValues.find(name);
            assert(it != globalValues.end());
        }
    }
    return it->second;
}

template<typename From, typename To>
std::vector<To> map(const std::vector<From>& from, To (&func)(const From&)) {
    std::vector<To> to;
    to.reserve(from.size());
    for (const auto& e : from) to.emplace_back(func(e));
    return to;
}

void codegen(const TypeDecl& decl);

llvm::Type* toIR(const Type& type) {
    switch (type.getKind()) {
        case TypeKind::BasicType: {
            const auto& name = type.getBasicType().name;
            if (name == "void") return llvm::Type::getVoidTy(ctx);
            if (name == "bool") return llvm::Type::getInt1Ty(ctx);
            if (name == "char") return llvm::Type::getInt8Ty(ctx);
            if (name == "int" || name == "uint") return llvm::Type::getInt32Ty(ctx);
            if (name == "int8" || name == "uint8") return llvm::Type::getInt8Ty(ctx);
            if (name == "int16" || name == "uint16") return llvm::Type::getInt16Ty(ctx);
            if (name == "int32" || name == "uint32") return llvm::Type::getInt32Ty(ctx);
            if (name == "int64" || name == "uint64") return llvm::Type::getInt64Ty(ctx);
            auto it = structs.find(name);
            if (it == structs.end()) {
                // Custom type that has not been declared yet, search for it in the symbol table.
                codegen(findInSymbolTable(name, SrcLoc::invalid()).getTypeDecl());
                it = structs.find(name);
            }
            return it->second.first;
        }
        case TypeKind::ArrayType: {
            const auto& array = type.getArrayType();
            return llvm::ArrayType::get(toIR(*array.elementType), array.size);
        }
        case TypeKind::TupleType:
            assert(false && "IRGen doesn't support tuple types yet");
        case TypeKind::FuncType:
            assert(false && "IRGen doesn't support function types yet");
        case TypeKind::PtrType: {
            auto* pointeeType = toIR(*type.getPtrType().pointeeType);
            if (!pointeeType->isVoidTy()) return llvm::PointerType::get(pointeeType, 0);
            else return llvm::PointerType::get(llvm::Type::getInt8Ty(ctx), 0);
        }
    }
}

llvm::Value* codegen(const Expr& expr);
llvm::Value* codegenLvalue(const Expr& expr);

llvm::Value* codegen(const VariableExpr& expr) {
    auto* value = findValue(expr.identifier);
    if (auto* arg = llvm::dyn_cast<llvm::Argument>(value)) return arg;
    return builder.CreateLoad(value, expr.identifier);
}

llvm::Value* codegenLvalue(const VariableExpr& expr) {
    return findValue(expr.identifier);
}

llvm::Value* codegen(const StrLiteralExpr& expr) {
    return builder.CreateGlobalStringPtr(expr.value);
}

llvm::Value* codegen(const IntLiteralExpr& expr, const Expr& parent) {
    return llvm::ConstantInt::getSigned(toIR(parent.getType()), expr.value);
}

llvm::Value* codegen(const BoolLiteralExpr& expr) {
    return expr.value ? llvm::ConstantInt::getTrue(ctx) : llvm::ConstantInt::getFalse(ctx);
}

llvm::Value* codegen(const NullLiteralExpr& expr, const Expr& parent) {
    return llvm::ConstantPointerNull::get(llvm::cast<llvm::PointerType>(toIR(parent.getType())));
}

using CreateNegFunc       = decltype(&llvm::IRBuilder<>::CreateNeg);
using CreateICmpFunc      = decltype(&llvm::IRBuilder<>::CreateICmpEQ);
using CreateAddSubMulFunc = decltype(&llvm::IRBuilder<>::CreateAdd);
using CreateDivFunc       = decltype(&llvm::IRBuilder<>::CreateSDiv);

llvm::Value* codegenPrefixOp(const PrefixExpr& expr, CreateNegFunc intFunc) {
    return (builder.*intFunc)(codegen(*expr.operand), "", false, false);
}

llvm::Value* codegen(const PrefixExpr& expr) {
    switch (expr.op.rawValue) {
        case PLUS:  return codegen(*expr.operand);
        case MINUS: return codegenPrefixOp(expr, &llvm::IRBuilder<>::CreateNeg);
        case STAR:  return builder.CreateLoad(codegen(*expr.operand));
        case AND:   return codegenLvalue(*expr.operand);
        default:    assert(false);
    }
}

llvm::Value* codegenLvalue(const PrefixExpr& expr) {
    switch (expr.op.rawValue) {
        case STAR:  return codegen(*expr.operand);
        default:    assert(false);
    }
}

llvm::Value* codegenBinaryOp(const BinaryExpr& expr, CreateICmpFunc intFunc) {
    llvm::Value* lhs = codegen(*expr.left);
    llvm::Value* rhs = codegen(*expr.right);
    return (builder.*intFunc)(lhs, rhs, "");
}

llvm::Value* codegenBinaryOp(const BinaryExpr& expr, CreateAddSubMulFunc intFunc) {
    llvm::Value* lhs = codegen(*expr.left);
    llvm::Value* rhs = codegen(*expr.right);
    return (builder.*intFunc)(lhs, rhs, "", false, false);
}

llvm::Value* codegenBinaryOp(const BinaryExpr& expr, CreateDivFunc intFunc) {
    llvm::Value* lhs = codegen(*expr.left);
    llvm::Value* rhs = codegen(*expr.right);
    return (builder.*intFunc)(lhs, rhs, "", false);
}

llvm::Value* codegen(const BinaryExpr& expr) {
    assert(expr.left->getType().isImplicitlyConvertibleTo(expr.right->getType())
        || expr.right->getType().isImplicitlyConvertibleTo(expr.left->getType()));

    switch (expr.op.rawValue) {
{'fraction_non_alphanumeric': 0.1586648738432334, 'fraction_numerical': 0.004429328482164044, 'mean_word_length': 3.8912959381044487, 'pattern_counts': {'":': 0, '<': 51, '<?xml version=': 0, '>': 65, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2054722', 'n_tokens_mistral': 3970, 'n_tokens_neox': 3999, 'n_words': 848}