mirror of https://github.com/exaloop/codon.git
297 lines
10 KiB
C++
297 lines
10 KiB
C++
#pragma once
|
|
|
|
#include "llvm.h"
|
|
#include "sir/sir.h"
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
namespace codon {
|
|
namespace ir {
|
|
|
|
class LLVMVisitor : public util::ConstVisitor {
|
|
private:
|
|
template <typename V> using CacheBase = std::unordered_map<id_t, V *>;
|
|
template <typename K, typename V> class Cache : public CacheBase<V> {
|
|
public:
|
|
using CacheBase<V>::CacheBase;
|
|
|
|
V *operator[](const K *key) {
|
|
auto it = CacheBase<V>::find(key->getId());
|
|
return (it != CacheBase<V>::end()) ? it->second : nullptr;
|
|
}
|
|
|
|
void insert(const K *key, V *value) { CacheBase<V>::emplace(key->getId(), value); }
|
|
};
|
|
|
|
struct CoroData {
|
|
/// Coroutine promise (where yielded values are stored)
|
|
llvm::Value *promise;
|
|
/// Coroutine handle
|
|
llvm::Value *handle;
|
|
/// Coroutine cleanup block
|
|
llvm::BasicBlock *cleanup;
|
|
/// Coroutine suspend block
|
|
llvm::BasicBlock *suspend;
|
|
/// Coroutine exit block
|
|
llvm::BasicBlock *exit;
|
|
};
|
|
|
|
struct NestableData {
|
|
int sequenceNumber;
|
|
|
|
NestableData() : sequenceNumber(-1) {}
|
|
};
|
|
|
|
struct LoopData : NestableData {
|
|
/// Block to branch to in case of "break"
|
|
llvm::BasicBlock *breakBlock;
|
|
/// Block to branch to in case of "continue"
|
|
llvm::BasicBlock *continueBlock;
|
|
/// Loop id
|
|
id_t loopId;
|
|
|
|
LoopData(llvm::BasicBlock *breakBlock, llvm::BasicBlock *continueBlock, id_t loopId)
|
|
: NestableData(), breakBlock(breakBlock), continueBlock(continueBlock),
|
|
loopId(loopId) {}
|
|
};
|
|
|
|
struct TryCatchData : NestableData {
|
|
/// Possible try-catch states when reaching finally block
|
|
enum State { NOT_THROWN = 0, THROWN, CAUGHT, RETURN, BREAK, CONTINUE };
|
|
/// Exception block
|
|
llvm::BasicBlock *exceptionBlock;
|
|
/// Exception route block
|
|
llvm::BasicBlock *exceptionRouteBlock;
|
|
/// Finally start block
|
|
llvm::BasicBlock *finallyBlock;
|
|
/// Try-catch catch types
|
|
std::vector<types::Type *> catchTypes;
|
|
/// Try-catch handlers, corresponding to catch types
|
|
std::vector<llvm::BasicBlock *> handlers;
|
|
/// Exception state flag (see "State")
|
|
llvm::Value *excFlag;
|
|
/// Storage for caught exception
|
|
llvm::Value *catchStore;
|
|
/// How far to delegate up the finally chain
|
|
llvm::Value *delegateDepth;
|
|
/// Storage for postponed return
|
|
llvm::Value *retStore;
|
|
/// Loop being manipulated
|
|
llvm::Value *loopSequence;
|
|
|
|
TryCatchData()
|
|
: NestableData(), exceptionBlock(nullptr), exceptionRouteBlock(nullptr),
|
|
finallyBlock(nullptr), catchTypes(), handlers(), excFlag(nullptr),
|
|
catchStore(nullptr), delegateDepth(nullptr), retStore(nullptr),
|
|
loopSequence(nullptr) {}
|
|
};
|
|
|
|
struct DebugInfo {
|
|
/// LLVM debug info builder
|
|
std::unique_ptr<llvm::DIBuilder> builder;
|
|
/// Current compilation unit
|
|
llvm::DICompileUnit *unit;
|
|
/// Whether we are compiling in debug mode
|
|
bool debug;
|
|
/// Program command-line flags
|
|
std::string flags;
|
|
|
|
explicit DebugInfo(bool debug, const std::string &flags)
|
|
: builder(), unit(nullptr), debug(debug), flags(flags) {}
|
|
|
|
llvm::DIFile *getFile(const std::string &path);
|
|
};
|
|
|
|
/// LLVM context used for compilation
|
|
llvm::LLVMContext context;
|
|
/// LLVM IR builder used for constructing LLVM IR
|
|
llvm::IRBuilder<> builder;
|
|
/// Module we are compiling
|
|
std::unique_ptr<llvm::Module> module;
|
|
/// Current function we are compiling
|
|
llvm::Function *func;
|
|
/// Current basic block we are compiling
|
|
llvm::BasicBlock *block;
|
|
/// Last compiled value
|
|
llvm::Value *value;
|
|
/// LLVM values corresponding to IR variables
|
|
Cache<Var, llvm::Value> vars;
|
|
/// LLVM functions corresponding to IR functions
|
|
Cache<Func, llvm::Function> funcs;
|
|
/// Coroutine data, if current function is a coroutine
|
|
CoroData coro;
|
|
/// Loop data stack, containing break/continue blocks
|
|
std::vector<LoopData> loops;
|
|
/// Try-catch data stack
|
|
std::vector<TryCatchData> trycatch;
|
|
/// Debug information
|
|
DebugInfo db;
|
|
/// LLVM target machine
|
|
std::unique_ptr<llvm::TargetMachine> machine;
|
|
|
|
llvm::DIType *
|
|
getDITypeHelper(types::Type *t,
|
|
std::unordered_map<std::string, llvm::DICompositeType *> &cache);
|
|
|
|
/// GC allocation functions
|
|
llvm::FunctionCallee makeAllocFunc(bool atomic);
|
|
/// Personality function for exception handling
|
|
llvm::FunctionCallee makePersonalityFunc();
|
|
/// Exception allocation function
|
|
llvm::FunctionCallee makeExcAllocFunc();
|
|
/// Exception throw function
|
|
llvm::FunctionCallee makeThrowFunc();
|
|
/// Program termination function
|
|
llvm::FunctionCallee makeTerminateFunc();
|
|
|
|
// Try-catch types and utilities
|
|
llvm::StructType *getTypeInfoType();
|
|
llvm::StructType *getPadType();
|
|
llvm::StructType *getExceptionType();
|
|
llvm::GlobalVariable *getTypeIdxVar(const std::string &name);
|
|
llvm::GlobalVariable *getTypeIdxVar(types::Type *catchType);
|
|
int getTypeIdx(types::Type *catchType = nullptr);
|
|
|
|
// General function helpers
|
|
llvm::Value *call(llvm::FunctionCallee callee, llvm::ArrayRef<llvm::Value *> args);
|
|
void makeLLVMFunction(const Func *);
|
|
void makeYield(llvm::Value *value = nullptr, bool finalYield = false);
|
|
std::string buildLLVMCodeString(const LLVMFunc *);
|
|
void callStage(const PipelineFlow::Stage *stage);
|
|
void codegenPipeline(const std::vector<const PipelineFlow::Stage *> &stages,
|
|
unsigned where = 0);
|
|
|
|
// Loop and try-catch state
|
|
void enterLoop(LoopData data);
|
|
void exitLoop();
|
|
void enterTryCatch(TryCatchData data);
|
|
void exitTryCatch();
|
|
TryCatchData *getInnermostTryCatch();
|
|
TryCatchData *getInnermostTryCatchBeforeLoop();
|
|
|
|
// LLVM passes
|
|
void applyDebugTransformations();
|
|
void runLLVMOptimizationPasses();
|
|
void runLLVMPipeline();
|
|
|
|
public:
|
|
LLVMVisitor(bool debug = false, const std::string &flags = "");
|
|
|
|
llvm::LLVMContext &getContext() { return context; }
|
|
llvm::IRBuilder<> &getBuilder() { return builder; }
|
|
llvm::Module *getModule() { return module.get(); }
|
|
llvm::FunctionCallee getFunc() { return func; }
|
|
llvm::BasicBlock *getBlock() { return block; }
|
|
llvm::Value *getValue() { return value; }
|
|
Cache<Var, llvm::Value> &getVars() { return vars; }
|
|
Cache<Func, llvm::Function> &getFuncs() { return funcs; }
|
|
CoroData &getCoro() { return coro; }
|
|
std::vector<LoopData> &getLoops() { return loops; }
|
|
std::vector<TryCatchData> &getTryCatch() { return trycatch; }
|
|
DebugInfo &getDebugInfo() { return db; }
|
|
|
|
void setFunc(llvm::Function *f) { func = f; }
|
|
void setBlock(llvm::BasicBlock *b) { block = b; }
|
|
void setValue(llvm::Value *v) { value = v; }
|
|
|
|
/// Sets current debug info based on a given node.
|
|
/// @param node the node whose debug info to use
|
|
void setDebugInfoForNode(const Node *node);
|
|
|
|
/// Compiles a given IR node, updating the internal
|
|
/// LLVM value and/or function as a result.
|
|
/// @param node the node to compile
|
|
void process(const Node *node);
|
|
|
|
/// Performs LLVM's module verification on the contained module.
|
|
/// Causes an assertion failure if verification fails.
|
|
void verify();
|
|
/// Dumps the unoptimized module IR to a file.
|
|
/// @param filename name of file to write IR to
|
|
void dump(const std::string &filename = "_dump.ll");
|
|
/// Writes module as native object file.
|
|
/// @param filename the .o file to write to
|
|
void writeToObjectFile(const std::string &filename);
|
|
/// Writes module as LLVM bitcode file.
|
|
/// @param filename the .bc file to write to
|
|
void writeToBitcodeFile(const std::string &filename);
|
|
/// Writes module as LLVM IR file.
|
|
/// @param filename the .ll file to write to
|
|
void writeToLLFile(const std::string &filename, bool optimize = true);
|
|
/// Writes module as native executable. Invokes an
|
|
/// external linker to generate the final executable.
|
|
/// @param filename the file to write to
|
|
/// @param libs library names to link
|
|
void writeToExecutable(const std::string &filename,
|
|
const std::vector<std::string> &libs = {});
|
|
/// Runs optimization passes on module and writes the result
|
|
/// to the specified file. The output type is determined by
|
|
/// the file extension (.ll for LLVM IR, .bc for LLVM bitcode
|
|
/// .o or .obj for object file, other for executable).
|
|
/// @param filename name of the file to write to
|
|
/// @param libs library names to link to, if creating executable
|
|
void compile(const std::string &filename, const std::vector<std::string> &libs = {});
|
|
/// Runs optimization passes on module and executes it.
|
|
/// @param args vector of arguments to program
|
|
/// @param libs vector of libraries to load
|
|
/// @param envp program environment
|
|
void run(const std::vector<std::string> &args = {},
|
|
const std::vector<std::string> &libs = {},
|
|
const char *const *envp = nullptr);
|
|
|
|
/// Get LLVM type from IR type
|
|
/// @param t the IR type
|
|
/// @return corresponding LLVM type
|
|
llvm::Type *getLLVMType(types::Type *t);
|
|
/// Get the LLVM debug info type from the IR type
|
|
/// @param t the IR type
|
|
/// @return corresponding LLVM DI type
|
|
llvm::DIType *getDIType(types::Type *t);
|
|
|
|
LoopData *getLoopData(id_t loopId);
|
|
|
|
void visit(const Module *) override;
|
|
void visit(const BodiedFunc *) override;
|
|
void visit(const ExternalFunc *) override;
|
|
void visit(const InternalFunc *) override;
|
|
void visit(const LLVMFunc *) override;
|
|
void visit(const Var *) override;
|
|
void visit(const VarValue *) override;
|
|
void visit(const PointerValue *) override;
|
|
|
|
void visit(const IntConst *) override;
|
|
void visit(const FloatConst *) override;
|
|
void visit(const BoolConst *) override;
|
|
void visit(const StringConst *) override;
|
|
void visit(const dsl::CustomConst *) override;
|
|
|
|
void visit(const SeriesFlow *) override;
|
|
void visit(const IfFlow *) override;
|
|
void visit(const WhileFlow *) override;
|
|
void visit(const ForFlow *) override;
|
|
void visit(const ImperativeForFlow *) override;
|
|
void visit(const TryCatchFlow *) override;
|
|
void visit(const PipelineFlow *) override;
|
|
void visit(const dsl::CustomFlow *) override;
|
|
|
|
void visit(const AssignInstr *) override;
|
|
void visit(const ExtractInstr *) override;
|
|
void visit(const InsertInstr *) override;
|
|
void visit(const CallInstr *) override;
|
|
void visit(const StackAllocInstr *) override;
|
|
void visit(const TypePropertyInstr *) override;
|
|
void visit(const YieldInInstr *) override;
|
|
void visit(const TernaryInstr *) override;
|
|
void visit(const BreakInstr *) override;
|
|
void visit(const ContinueInstr *) override;
|
|
void visit(const ReturnInstr *) override;
|
|
void visit(const YieldInstr *) override;
|
|
void visit(const ThrowInstr *) override;
|
|
void visit(const FlowInstr *) override;
|
|
void visit(const dsl::CustomInstr *) override;
|
|
};
|
|
|
|
} // namespace ir
|
|
} // namespace codon
|