Get awful LLVM code generation working.
This commit is contained in:
parent
e579078f73
commit
37f5e53a59
|
@ -1,6 +1,12 @@
|
||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.0)
|
||||||
project(lily)
|
project(lily)
|
||||||
|
|
||||||
|
find_package(LLVM REQUIRED CONFIG)
|
||||||
|
llvm_map_components_to_libnames(LLVM_LIBS core x86asmparser x86asmprinter x86codegen)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 14)
|
set(CMAKE_CXX_STANDARD 14)
|
||||||
add_executable(lily src/main.cpp src/parser.cpp src/parser.c src/type.cpp src/type_manager.cpp src/ast.cpp src/type_checker.cpp src/pattern.cpp src/gmachine.cpp src/compiler.cpp)
|
add_executable(lily src/main.cpp src/parser.cpp src/parser.c src/type.cpp src/type_manager.cpp src/ast.cpp src/type_checker.cpp src/pattern.cpp src/gmachine.cpp src/compiler.cpp src/llvm.cpp)
|
||||||
target_include_directories(lily PUBLIC src)
|
target_include_directories(lily PUBLIC src)
|
||||||
|
target_include_directories(lily PUBLIC ${LLVM_INCLUDE_DIRS})
|
||||||
|
target_compile_definitions(lily PUBLIC ${LLVM_DEFINITIONS})
|
||||||
|
target_link_libraries(lily ${LLVM_LIBS})
|
||||||
|
|
181
runtime.c
Normal file
181
runtime.c
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <memory.h>
|
||||||
|
|
||||||
|
struct node_parent {
|
||||||
|
char tag;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct stack {
|
||||||
|
int32_t size;
|
||||||
|
int32_t count;
|
||||||
|
struct node_parent** data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct node_app {
|
||||||
|
char tag;
|
||||||
|
struct node_parent* left;
|
||||||
|
struct node_parent* right;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct node_num {
|
||||||
|
char tag;
|
||||||
|
int32_t value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct node_global {
|
||||||
|
char tag;
|
||||||
|
int32_t arity;
|
||||||
|
void (*function)(struct stack*);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct node_ind {
|
||||||
|
char tag;
|
||||||
|
struct node_parent* next;
|
||||||
|
};
|
||||||
|
|
||||||
|
void stack_init(struct stack* stack) {
|
||||||
|
stack->size = 16;
|
||||||
|
stack->count = 0;
|
||||||
|
stack->data = malloc(sizeof(*stack->data) * stack->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stack_free(struct stack* stack) {
|
||||||
|
free(stack->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stack_push(struct stack* stack, struct node_parent* node) {
|
||||||
|
printf("pushing\n");
|
||||||
|
assert(stack->count < stack->size);
|
||||||
|
stack->data[stack->count++] = node;
|
||||||
|
printf("new size: %d\n", stack->count);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node_parent* stack_peek(struct stack* stack, int32_t offset) {
|
||||||
|
assert(offset + 1 <= stack->count);
|
||||||
|
return stack->data[stack->count - offset - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node_parent* stack_pop(struct stack* stack) {
|
||||||
|
printf("popping\nnew size: %d\n", stack->count - 1);
|
||||||
|
assert(stack->count > 0);
|
||||||
|
return stack->data[--stack->count];
|
||||||
|
}
|
||||||
|
|
||||||
|
void stack_popn(struct stack* stack, int32_t count) {
|
||||||
|
assert(stack->count >= count);
|
||||||
|
stack->count -= count;
|
||||||
|
printf("popping %d\nnew size: %d\n", count, stack->count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stack_update(struct stack* stack, int32_t offset) {
|
||||||
|
assert(stack->count >= offset + 2);
|
||||||
|
struct node_parent* to_replace = stack->data[stack->count - 1 - 1 - offset];
|
||||||
|
struct node_parent* to_use = stack->data[stack->count - 1];
|
||||||
|
memcpy(to_replace, to_use, sizeof(struct node_app));
|
||||||
|
stack->count--;
|
||||||
|
printf("updating\nnew size: %d\n", stack->count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stack_alloc(struct stack* stack, int32_t count) {
|
||||||
|
assert(stack->count + count <= stack->size);
|
||||||
|
for(int32_t i = 0; i < count; i++) {
|
||||||
|
stack->data[stack->count++] = malloc(sizeof(struct node_app));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stack_slide(struct stack* stack, int32_t count) {
|
||||||
|
assert(stack->count > count);
|
||||||
|
stack->data[stack->count - 1 - count] = stack->data[stack->count - 1];
|
||||||
|
stack->count -= count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void stack_set(struct stack* stack, int32_t index, struct node_parent* parent) {
|
||||||
|
assert(stack->count > index);
|
||||||
|
stack->data[stack->count - 1 - index] = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t stack_size(struct stack* stack) {
|
||||||
|
return stack->count;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node_parent* malloc_node_num(int32_t value) {
|
||||||
|
printf("alloced num %d\n", value);
|
||||||
|
struct node_num* node = malloc(sizeof(struct node_app));
|
||||||
|
node->tag = 0;
|
||||||
|
node->value = value;
|
||||||
|
return (struct node_parent*) node;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node_parent* malloc_node_app(struct node_parent* left, struct node_parent* right) {
|
||||||
|
printf("allocated application\n");
|
||||||
|
struct node_app* node = malloc(sizeof(struct node_app));
|
||||||
|
node->tag = 1;
|
||||||
|
node->left = left;
|
||||||
|
node->right = right;
|
||||||
|
return (struct node_parent*) node;
|
||||||
|
}
|
||||||
|
struct node_parent* malloc_node_global(int32_t arity, void (*function)(struct stack*)) {
|
||||||
|
printf("allocated global with arity %d\n", arity);
|
||||||
|
struct node_global* node = malloc(sizeof(struct node_app));
|
||||||
|
node->tag = 2;
|
||||||
|
node->arity = arity;
|
||||||
|
node->function = function;
|
||||||
|
return (struct node_parent*) node;
|
||||||
|
}
|
||||||
|
struct node_parent* malloc_node_indirect(struct node_parent* target) {
|
||||||
|
printf("allocated indirection node\n");
|
||||||
|
struct node_ind* node = malloc(sizeof(struct node_app));
|
||||||
|
node->tag = 3;
|
||||||
|
node->next = target;
|
||||||
|
return (struct node_parent*) node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unwind(struct stack* stack) {
|
||||||
|
while(1) {
|
||||||
|
assert(stack_size(stack) != 0);
|
||||||
|
struct node_parent* node = stack_peek(stack, 0);
|
||||||
|
if(node->tag == 0) {
|
||||||
|
return;
|
||||||
|
} else if(node->tag == 1) {
|
||||||
|
printf("unwinding an application\n");
|
||||||
|
stack_push(stack, ((struct node_app*) node)->left);
|
||||||
|
} else if(node->tag == 2) {
|
||||||
|
struct node_global* global = (struct node_global*) node;
|
||||||
|
if(stack->size > global->arity) {
|
||||||
|
for(int i = 0; i < global->arity; i++) {
|
||||||
|
struct node_app* app = (struct node_app*) stack_peek(stack, i + 1);
|
||||||
|
stack_set(stack, i + 1, app->right);
|
||||||
|
}
|
||||||
|
global->function(stack);
|
||||||
|
} else {
|
||||||
|
stack_popn(stack, stack_size(stack) - 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if(node->tag == 3) {
|
||||||
|
struct node_ind* ind = (struct node_ind*) node;
|
||||||
|
stack_pop(stack);
|
||||||
|
stack_push(stack, ind->next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node_parent* eval(struct node_parent* start) {
|
||||||
|
struct stack new_stack;
|
||||||
|
stack_init(&new_stack);
|
||||||
|
stack_push(&new_stack, start);
|
||||||
|
unwind(&new_stack);
|
||||||
|
struct node_parent* final_node = stack_pop(&new_stack);
|
||||||
|
stack_free(&new_stack);
|
||||||
|
return final_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void main_supercomb(struct stack* stack);
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
struct node_parent* result = eval(malloc_node_global(0, main_supercomb));
|
||||||
|
if(result->tag == 0) {
|
||||||
|
printf("resulting number: %d\n", ((struct node_num*) result)->value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -95,7 +95,7 @@ namespace lily {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ast_num::compile(instruction_manager& mgr, std::vector<instruction*>& into, std::shared_ptr<compile_env> env) {
|
void ast_num::compile(instruction_manager& mgr, std::vector<instruction*>& into, std::shared_ptr<compile_env> env) {
|
||||||
into.push_back(mgr.add_instruction<instruction_push_int>(3));
|
into.push_back(mgr.add_instruction<instruction_push_int>(num));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ast_var::compile(instruction_manager& mgr, std::vector<instruction*>& into, std::shared_ptr<compile_env> env) {
|
void ast_var::compile(instruction_manager& mgr, std::vector<instruction*>& into, std::shared_ptr<compile_env> env) {
|
||||||
|
|
|
@ -86,4 +86,102 @@ namespace lily {
|
||||||
os << "jump";
|
os << "jump";
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void instruction_slide::gen_llvm(llvm_context& ctx) {
|
||||||
|
llvm::Value* stack = ctx.get_current_function()->arg_begin();
|
||||||
|
builder.CreateCall(stack_slide_func, { stack, get_int32_constant(amount) });
|
||||||
|
}
|
||||||
|
|
||||||
|
void instruction_alloc::gen_llvm(llvm_context& ctx) {
|
||||||
|
llvm::Value* stack = ctx.get_current_function()->arg_begin();
|
||||||
|
builder.CreateCall(stack_alloc_func, { stack, get_int32_constant(amount) });
|
||||||
|
}
|
||||||
|
|
||||||
|
void instruction_pop::gen_llvm(llvm_context& ctx) {
|
||||||
|
llvm::Value* stack = ctx.get_current_function()->arg_begin();
|
||||||
|
builder.CreateCall(stack_popn_func, { stack, get_int32_constant(amount) });
|
||||||
|
}
|
||||||
|
|
||||||
|
void instruction_unwind::gen_llvm(llvm_context& ctx) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void instruction_push_global::gen_llvm(llvm_context& ctx) {
|
||||||
|
llvm::Value* stack = ctx.get_current_function()->arg_begin();
|
||||||
|
llvm::Value* new_node = builder.CreateCall(malloc_node_global_func,
|
||||||
|
{ get_int8_constant(2), ctx.get_supercombinator(name) }, "temp");
|
||||||
|
// TODO get arity
|
||||||
|
builder.CreateCall(stack_push_func, { stack, new_node });
|
||||||
|
}
|
||||||
|
|
||||||
|
void instruction_push_int::gen_llvm(llvm_context& ctx) {
|
||||||
|
llvm::Value* stack = ctx.get_current_function()->arg_begin();
|
||||||
|
llvm::Value* new_node = builder.CreateCall(malloc_node_num_func,
|
||||||
|
{ get_int32_constant(value) });
|
||||||
|
builder.CreateCall(stack_push_func, { stack, new_node });
|
||||||
|
}
|
||||||
|
|
||||||
|
void instruction_push_str::gen_llvm(llvm_context& ctx) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void instruction_push::gen_llvm(llvm_context& ctx) {
|
||||||
|
llvm::Value* stack = ctx.get_current_function()->arg_begin();
|
||||||
|
llvm::Value* peeked = builder.CreateCall(stack_peek_func, { stack, get_int32_constant(offset) }, "temp");
|
||||||
|
builder.CreateCall(stack_push_func, { stack, peeked });
|
||||||
|
}
|
||||||
|
|
||||||
|
void instruction_mkapp::gen_llvm(llvm_context& ctx) {
|
||||||
|
llvm::Value* stack = ctx.get_current_function()->arg_begin();
|
||||||
|
llvm::Value* func = builder.CreateCall(stack_pop_func, { stack }, "temp");
|
||||||
|
llvm::Value* param = builder.CreateCall(stack_pop_func, { stack }, "temp");
|
||||||
|
llvm::Value* app_node = builder.CreateCall(malloc_node_app_func, { func, param }, "temp");
|
||||||
|
builder.CreateCall(stack_push_func, { stack, app_node });
|
||||||
|
}
|
||||||
|
|
||||||
|
void instruction_eval::gen_llvm(llvm_context& ctx) {
|
||||||
|
// ??
|
||||||
|
}
|
||||||
|
|
||||||
|
void instruction_op::gen_llvm(llvm_context& ctx) {
|
||||||
|
llvm::Value* stack = ctx.get_current_function()->arg_begin();
|
||||||
|
llvm::Value* param1 = builder.CreateCall(stack_pop_func, { stack }, "temp");
|
||||||
|
llvm::Value* param2 = builder.CreateCall(stack_pop_func, { stack }, "temp");
|
||||||
|
llvm::Value* param1_node_num = builder.CreatePointerCast(param1, llvm::PointerType::getUnqual(node_num_type), "temp");
|
||||||
|
llvm::Value* param2_node_num = builder.CreatePointerCast(param2, llvm::PointerType::getUnqual(node_num_type), "temp");
|
||||||
|
llvm::Value* param1_intptr = builder.CreateGEP(param1_node_num, { get_int32_constant(0), get_int32_constant(1) }, "temp");
|
||||||
|
llvm::Value* param2_intptr = builder.CreateGEP(param2_node_num, { get_int32_constant(0), get_int32_constant(1) }, "temp");
|
||||||
|
llvm::Value* param1_int = builder.CreateLoad(llvm::IntegerType::get(context, 32), param1_intptr, "temp");
|
||||||
|
llvm::Value* param2_int = builder.CreateLoad(llvm::IntegerType::get(context, 32), param2_intptr, "temp");
|
||||||
|
llvm::Value* op_result;
|
||||||
|
switch(op) {
|
||||||
|
case binop::add: op_result = builder.CreateAdd(param2_int, param1_int, "temp"); break;
|
||||||
|
case binop::subtract: op_result = builder.CreateSub(param2_int, param1_int, "temp"); break;
|
||||||
|
case binop::times: op_result = builder.CreateMul(param2_int, param1_int, "temp"); break;
|
||||||
|
case binop::divide: op_result = builder.CreateSDiv(param2_int, param1_int, "temp"); break;
|
||||||
|
}
|
||||||
|
llvm::Value* new_node = builder.CreateCall(malloc_node_num_func, { op_result }, "temp");
|
||||||
|
builder.CreateCall(stack_push_func, { stack, new_node });
|
||||||
|
}
|
||||||
|
|
||||||
|
void instruction_cond::gen_llvm(llvm_context& ctx) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void instruction_update::gen_llvm(llvm_context& ctx) {
|
||||||
|
llvm::Value* stack = ctx.get_current_function()->arg_begin();
|
||||||
|
builder.CreateCall(stack_update_func, { stack, get_int32_constant(offset) });
|
||||||
|
}
|
||||||
|
|
||||||
|
void instruction_pack::gen_llvm(llvm_context& ctx) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void instruction_split::gen_llvm(llvm_context& ctx) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void instruction_jump::gen_llvm(llvm_context& ctx) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,13 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "binop.hpp"
|
#include "binop.hpp"
|
||||||
|
#include "llvm.hpp"
|
||||||
|
|
||||||
namespace lily {
|
namespace lily {
|
||||||
struct instruction {
|
struct instruction {
|
||||||
virtual std::ostream& to_stream(std::ostream& os) = 0;
|
|
||||||
virtual ~instruction() = default;
|
virtual ~instruction() = default;
|
||||||
|
virtual std::ostream& to_stream(std::ostream& os) = 0;
|
||||||
|
virtual void gen_llvm(llvm_context& ctx) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, instruction& inst);
|
std::ostream& operator<<(std::ostream& os, instruction& inst);
|
||||||
|
@ -16,6 +18,7 @@ namespace lily {
|
||||||
|
|
||||||
instruction_slide(int a) : amount(a) {}
|
instruction_slide(int a) : amount(a) {}
|
||||||
std::ostream& to_stream(std::ostream& os);
|
std::ostream& to_stream(std::ostream& os);
|
||||||
|
void gen_llvm(llvm_context& ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct instruction_alloc : instruction {
|
struct instruction_alloc : instruction {
|
||||||
|
@ -23,6 +26,7 @@ namespace lily {
|
||||||
|
|
||||||
instruction_alloc(int a) : amount(a) {}
|
instruction_alloc(int a) : amount(a) {}
|
||||||
std::ostream& to_stream(std::ostream& os);
|
std::ostream& to_stream(std::ostream& os);
|
||||||
|
void gen_llvm(llvm_context& ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct instruction_pop : instruction {
|
struct instruction_pop : instruction {
|
||||||
|
@ -30,10 +34,12 @@ namespace lily {
|
||||||
|
|
||||||
instruction_pop(int a) : amount(a) {}
|
instruction_pop(int a) : amount(a) {}
|
||||||
std::ostream& to_stream(std::ostream& os);
|
std::ostream& to_stream(std::ostream& os);
|
||||||
|
void gen_llvm(llvm_context& ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct instruction_unwind : instruction {
|
struct instruction_unwind : instruction {
|
||||||
std::ostream& to_stream(std::ostream& os);
|
std::ostream& to_stream(std::ostream& os);
|
||||||
|
void gen_llvm(llvm_context& ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct instruction_push_global : instruction {
|
struct instruction_push_global : instruction {
|
||||||
|
@ -41,6 +47,7 @@ namespace lily {
|
||||||
|
|
||||||
instruction_push_global(std::string n) : name(std::move(n)) {}
|
instruction_push_global(std::string n) : name(std::move(n)) {}
|
||||||
std::ostream& to_stream(std::ostream& os);
|
std::ostream& to_stream(std::ostream& os);
|
||||||
|
void gen_llvm(llvm_context& ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct instruction_push_int : instruction {
|
struct instruction_push_int : instruction {
|
||||||
|
@ -48,6 +55,7 @@ namespace lily {
|
||||||
|
|
||||||
instruction_push_int(int v) : value(v) {}
|
instruction_push_int(int v) : value(v) {}
|
||||||
std::ostream& to_stream(std::ostream& os);
|
std::ostream& to_stream(std::ostream& os);
|
||||||
|
void gen_llvm(llvm_context& ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct instruction_push_str : instruction {
|
struct instruction_push_str : instruction {
|
||||||
|
@ -55,6 +63,7 @@ namespace lily {
|
||||||
|
|
||||||
instruction_push_str(std::string s) : str(std::move(s)) {}
|
instruction_push_str(std::string s) : str(std::move(s)) {}
|
||||||
std::ostream& to_stream(std::ostream& os);
|
std::ostream& to_stream(std::ostream& os);
|
||||||
|
void gen_llvm(llvm_context& ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct instruction_push : instruction {
|
struct instruction_push : instruction {
|
||||||
|
@ -62,16 +71,19 @@ namespace lily {
|
||||||
|
|
||||||
instruction_push(int o) : offset(o) {}
|
instruction_push(int o) : offset(o) {}
|
||||||
std::ostream& to_stream(std::ostream& os);
|
std::ostream& to_stream(std::ostream& os);
|
||||||
|
void gen_llvm(llvm_context& ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct instruction_mkapp : instruction {
|
struct instruction_mkapp : instruction {
|
||||||
|
|
||||||
std::ostream& to_stream(std::ostream& os);
|
std::ostream& to_stream(std::ostream& os);
|
||||||
|
void gen_llvm(llvm_context& ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct instruction_eval : instruction {
|
struct instruction_eval : instruction {
|
||||||
|
|
||||||
std::ostream& to_stream(std::ostream& os);
|
std::ostream& to_stream(std::ostream& os);
|
||||||
|
void gen_llvm(llvm_context& ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct instruction_op : instruction {
|
struct instruction_op : instruction {
|
||||||
|
@ -79,12 +91,14 @@ namespace lily {
|
||||||
|
|
||||||
instruction_op(binop o) : op(o) {}
|
instruction_op(binop o) : op(o) {}
|
||||||
std::ostream& to_stream(std::ostream& os);
|
std::ostream& to_stream(std::ostream& os);
|
||||||
|
void gen_llvm(llvm_context& ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct instruction_cond : instruction {
|
struct instruction_cond : instruction {
|
||||||
std::vector<instruction*> true_branch;
|
std::vector<instruction*> true_branch;
|
||||||
std::vector<instruction*> false_branch;
|
std::vector<instruction*> false_branch;
|
||||||
std::ostream& to_stream(std::ostream& os);
|
std::ostream& to_stream(std::ostream& os);
|
||||||
|
void gen_llvm(llvm_context& ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct instruction_update : instruction {
|
struct instruction_update : instruction {
|
||||||
|
@ -92,6 +106,7 @@ namespace lily {
|
||||||
|
|
||||||
instruction_update(int o) : offset(o) {}
|
instruction_update(int o) : offset(o) {}
|
||||||
std::ostream& to_stream(std::ostream& os);
|
std::ostream& to_stream(std::ostream& os);
|
||||||
|
void gen_llvm(llvm_context& ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct instruction_pack : instruction {
|
struct instruction_pack : instruction {
|
||||||
|
@ -101,6 +116,7 @@ namespace lily {
|
||||||
instruction_pack(int c, int a) :
|
instruction_pack(int c, int a) :
|
||||||
constructor(c), arity(a) {}
|
constructor(c), arity(a) {}
|
||||||
std::ostream& to_stream(std::ostream& os);
|
std::ostream& to_stream(std::ostream& os);
|
||||||
|
void gen_llvm(llvm_context& ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct instruction_split : instruction {
|
struct instruction_split : instruction {
|
||||||
|
@ -108,11 +124,13 @@ namespace lily {
|
||||||
|
|
||||||
instruction_split(int a) : arity(a) {}
|
instruction_split(int a) : arity(a) {}
|
||||||
std::ostream& to_stream(std::ostream& os);
|
std::ostream& to_stream(std::ostream& os);
|
||||||
|
void gen_llvm(llvm_context& ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct instruction_jump : instruction {
|
struct instruction_jump : instruction {
|
||||||
std::vector<std::vector<instruction*>> instructions;
|
std::vector<std::vector<instruction*>> instructions;
|
||||||
std::ostream& to_stream(std::ostream& os);
|
std::ostream& to_stream(std::ostream& os);
|
||||||
|
void gen_llvm(llvm_context& ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
class instruction_manager {
|
class instruction_manager {
|
||||||
|
|
237
src/llvm.cpp
Normal file
237
src/llvm.cpp
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
#include "llvm.hpp"
|
||||||
|
#include "error.hpp"
|
||||||
|
#include "llvm/IR/Verifier.h"
|
||||||
|
#include "llvm/IR/LegacyPassManager.h"
|
||||||
|
#include "llvm/Support/TargetSelect.h"
|
||||||
|
#include "llvm/Support/TargetRegistry.h"
|
||||||
|
#include "llvm/Support/raw_ostream.h"
|
||||||
|
#include "llvm/Support/FileSystem.h"
|
||||||
|
#include "llvm/Target/TargetOptions.h"
|
||||||
|
#include "llvm/Target/TargetMachine.h"
|
||||||
|
|
||||||
|
namespace lily {
|
||||||
|
llvm::LLVMContext context;
|
||||||
|
llvm::IRBuilder<> builder(context);
|
||||||
|
llvm::Module module("Lily", context);
|
||||||
|
|
||||||
|
llvm::StructType* stack_type;
|
||||||
|
llvm::PointerType* stack_pointer_type;
|
||||||
|
|
||||||
|
llvm::FunctionType* supercomb_function_type;
|
||||||
|
llvm::FunctionType* eval_function_type;
|
||||||
|
llvm::PointerType* supercomb_function_pointer_type;
|
||||||
|
|
||||||
|
llvm::Function* stack_init_func;
|
||||||
|
llvm::Function* stack_free_func;
|
||||||
|
llvm::Function* stack_push_func;
|
||||||
|
llvm::Function* stack_peek_func;
|
||||||
|
llvm::Function* stack_pop_func;
|
||||||
|
llvm::Function* stack_popn_func;
|
||||||
|
llvm::Function* stack_update_func;
|
||||||
|
llvm::Function* stack_alloc_func;
|
||||||
|
llvm::Function* stack_slide_func;
|
||||||
|
llvm::Function* stack_size_func;
|
||||||
|
|
||||||
|
llvm::Function* malloc_node_num_func;
|
||||||
|
llvm::Function* malloc_node_app_func;
|
||||||
|
llvm::Function* malloc_node_global_func;
|
||||||
|
llvm::Function* malloc_node_ind_func;
|
||||||
|
|
||||||
|
llvm::IntegerType* tag_type;
|
||||||
|
llvm::PointerType* node_pointer_type;
|
||||||
|
llvm::StructType* node_parent_type;
|
||||||
|
llvm::StructType* node_num_type;
|
||||||
|
llvm::StructType* node_app_type;
|
||||||
|
llvm::StructType* node_global_type;
|
||||||
|
llvm::StructType* node_indirect_type;
|
||||||
|
|
||||||
|
static void initialize_llvm() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Value* get_int32_constant(int value) {
|
||||||
|
return llvm::ConstantInt::get(context, llvm::APInt(32, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Value* get_int64_constant(long int value) {
|
||||||
|
return llvm::ConstantInt::get(context, llvm::APInt(64, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Value* get_int8_constant(char value) {
|
||||||
|
return llvm::ConstantInt::get(context, llvm::APInt(8, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Value* get_type_size(llvm::Type* type) {
|
||||||
|
llvm::PointerType* pointer = llvm::PointerType::getUnqual(type);
|
||||||
|
llvm::Value* null = llvm::ConstantPointerNull::get(pointer);
|
||||||
|
llvm::Value* ptr = builder.CreateGEP(type, null, {get_int32_constant(1), get_int32_constant(0)}, "temp");
|
||||||
|
llvm::Value* val = builder.CreatePtrToInt(ptr, llvm::IntegerType::get(context, 64), "temp");
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void initialize_functions() {
|
||||||
|
stack_init_func = llvm::Function::Create(
|
||||||
|
llvm::FunctionType::get(llvm::Type::getVoidTy(context), { stack_pointer_type }, false),
|
||||||
|
llvm::Function::LinkageTypes::ExternalLinkage,
|
||||||
|
"stack_init",
|
||||||
|
&module);
|
||||||
|
stack_free_func = llvm::Function::Create(
|
||||||
|
llvm::FunctionType::get(llvm::Type::getVoidTy(context), { stack_pointer_type }, false),
|
||||||
|
llvm::Function::LinkageTypes::ExternalLinkage,
|
||||||
|
"stack_free",
|
||||||
|
&module);
|
||||||
|
stack_push_func = llvm::Function::Create(
|
||||||
|
llvm::FunctionType::get(llvm::Type::getVoidTy(context), { stack_pointer_type, node_pointer_type }, false),
|
||||||
|
llvm::Function::LinkageTypes::ExternalLinkage,
|
||||||
|
"stack_push",
|
||||||
|
&module);
|
||||||
|
stack_peek_func = llvm::Function::Create(
|
||||||
|
llvm::FunctionType::get(node_pointer_type, { stack_pointer_type, llvm::IntegerType::get(context, 32) }, false),
|
||||||
|
llvm::Function::LinkageTypes::ExternalLinkage,
|
||||||
|
"stack_peek",
|
||||||
|
&module);
|
||||||
|
stack_pop_func = llvm::Function::Create(
|
||||||
|
llvm::FunctionType::get(node_pointer_type, { stack_pointer_type }, false),
|
||||||
|
llvm::Function::LinkageTypes::ExternalLinkage,
|
||||||
|
"stack_pop",
|
||||||
|
&module);
|
||||||
|
stack_popn_func = llvm::Function::Create(
|
||||||
|
llvm::FunctionType::get(llvm::Type::getVoidTy(context), { stack_pointer_type, llvm::IntegerType::get(context, 32) }, false),
|
||||||
|
llvm::Function::LinkageTypes::ExternalLinkage,
|
||||||
|
"stack_popn",
|
||||||
|
&module);
|
||||||
|
stack_update_func = llvm::Function::Create(
|
||||||
|
llvm::FunctionType::get(llvm::Type::getVoidTy(context), { stack_pointer_type, llvm::IntegerType::get(context, 32) }, false),
|
||||||
|
llvm::Function::LinkageTypes::ExternalLinkage,
|
||||||
|
"stack_update",
|
||||||
|
&module);
|
||||||
|
stack_alloc_func = llvm::Function::Create(
|
||||||
|
llvm::FunctionType::get(llvm::Type::getVoidTy(context), { stack_pointer_type, llvm::IntegerType::get(context, 32) }, false),
|
||||||
|
llvm::Function::LinkageTypes::ExternalLinkage,
|
||||||
|
"stack_alloc",
|
||||||
|
&module);
|
||||||
|
stack_slide_func = llvm::Function::Create(
|
||||||
|
llvm::FunctionType::get(llvm::Type::getVoidTy(context), { stack_pointer_type, llvm::IntegerType::get(context, 32) }, false),
|
||||||
|
llvm::Function::LinkageTypes::ExternalLinkage,
|
||||||
|
"stack_slide",
|
||||||
|
&module);
|
||||||
|
malloc_node_num_func = llvm::Function::Create(
|
||||||
|
llvm::FunctionType::get(llvm::PointerType::getUnqual(node_parent_type), { llvm::IntegerType::get(context, 32) }, false),
|
||||||
|
llvm::Function::LinkageTypes::ExternalLinkage,
|
||||||
|
"malloc_node_num",
|
||||||
|
&module);
|
||||||
|
malloc_node_app_func = llvm::Function::Create(
|
||||||
|
llvm::FunctionType::get(llvm::PointerType::getUnqual(node_parent_type), { node_pointer_type, node_pointer_type }, false),
|
||||||
|
llvm::Function::LinkageTypes::ExternalLinkage,
|
||||||
|
"malloc_node_app",
|
||||||
|
&module);
|
||||||
|
malloc_node_global_func = llvm::Function::Create(
|
||||||
|
llvm::FunctionType::get(llvm::PointerType::getUnqual(node_parent_type), { tag_type, supercomb_function_pointer_type }, false),
|
||||||
|
llvm::Function::LinkageTypes::ExternalLinkage,
|
||||||
|
"malloc_node_global",
|
||||||
|
&module);
|
||||||
|
malloc_node_ind_func = llvm::Function::Create(
|
||||||
|
llvm::FunctionType::get(llvm::PointerType::getUnqual(node_parent_type), { node_pointer_type }, false),
|
||||||
|
llvm::Function::LinkageTypes::ExternalLinkage,
|
||||||
|
"malloc_node_indirect",
|
||||||
|
&module);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void initialize_types() {
|
||||||
|
stack_type = llvm::StructType::create(context, "stack");
|
||||||
|
stack_pointer_type = llvm::PointerType::getUnqual(stack_type);
|
||||||
|
|
||||||
|
tag_type = llvm::IntegerType::get(context, 8);
|
||||||
|
node_parent_type = llvm::StructType::create(context, "node_parent");
|
||||||
|
node_pointer_type = llvm::PointerType::getUnqual(node_parent_type);
|
||||||
|
node_num_type = llvm::StructType::create(context, "node_num");
|
||||||
|
node_app_type = llvm::StructType::create(context, "node_app");
|
||||||
|
node_global_type = llvm::StructType::create(context, "node_global");
|
||||||
|
node_indirect_type = llvm::StructType::create(context, "node_indirect");
|
||||||
|
|
||||||
|
supercomb_function_type = llvm::FunctionType::get(llvm::Type::getVoidTy(context), { stack_pointer_type }, false);
|
||||||
|
supercomb_function_pointer_type = llvm::PointerType::getUnqual(supercomb_function_type);
|
||||||
|
|
||||||
|
stack_type->setBody(llvm::IntegerType::get(context, 32), llvm::IntegerType::get(context, 32), llvm::PointerType::getUnqual(node_pointer_type));
|
||||||
|
node_parent_type->setBody(tag_type);
|
||||||
|
node_num_type->setBody(tag_type, llvm::IntegerType::get(context, 32));
|
||||||
|
node_app_type->setBody(tag_type, node_pointer_type, node_pointer_type);
|
||||||
|
node_global_type->setBody(tag_type, tag_type, supercomb_function_pointer_type);
|
||||||
|
node_indirect_type->setBody(tag_type, node_pointer_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void llvm_generate(const std::string& filename) {
|
||||||
|
std::string targetTriple = llvm::sys::getDefaultTargetTriple();
|
||||||
|
|
||||||
|
llvm::InitializeNativeTarget();
|
||||||
|
llvm::InitializeNativeTargetAsmParser();
|
||||||
|
llvm::InitializeNativeTargetAsmPrinter();
|
||||||
|
|
||||||
|
std::string error;
|
||||||
|
const llvm::Target* target =
|
||||||
|
llvm::TargetRegistry::lookupTarget(targetTriple, error);
|
||||||
|
if (!target) {
|
||||||
|
std::cerr << error << std::endl;
|
||||||
|
} else {
|
||||||
|
std::string cpu = "generic";
|
||||||
|
std::string features = "";
|
||||||
|
llvm::TargetOptions options;
|
||||||
|
llvm::TargetMachine* targetMachine =
|
||||||
|
target->createTargetMachine(targetTriple, cpu, features,
|
||||||
|
options, llvm::Optional<llvm::Reloc::Model>());
|
||||||
|
|
||||||
|
module.setDataLayout(targetMachine->createDataLayout());
|
||||||
|
module.setTargetTriple(targetTriple);
|
||||||
|
|
||||||
|
std::error_code ec;
|
||||||
|
llvm::raw_fd_ostream file(filename, ec, llvm::sys::fs::F_None);
|
||||||
|
if (ec) {
|
||||||
|
std::cerr << "Could not open output file: " << ec.message() << std::endl;
|
||||||
|
} else {
|
||||||
|
llvm::TargetMachine::CodeGenFileType type = llvm::TargetMachine::CGFT_ObjectFile;
|
||||||
|
llvm::legacy::PassManager pm;
|
||||||
|
if (targetMachine->addPassesToEmitFile(pm, file, NULL, type)) {
|
||||||
|
std::cerr << "Unable to emit target code" << std::endl;
|
||||||
|
} else {
|
||||||
|
pm.run(module);
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void llvm_init() {
|
||||||
|
static bool initialized = false;
|
||||||
|
if(!initialized) {
|
||||||
|
initialize_llvm();
|
||||||
|
initialize_types();
|
||||||
|
initialize_functions();
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Function* llvm_context::get_supercombinator(const std::string& name) {
|
||||||
|
if(!supercombinators.count(name)) throw error("unknown supercombinator");
|
||||||
|
return supercombinators.find(name)->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
void llvm_context::add_supercombinator(const std::string& name) {
|
||||||
|
if(supercombinators.count(name)) throw error("re-creating supercombinator");
|
||||||
|
llvm::Function* new_function = llvm::Function::Create(
|
||||||
|
supercomb_function_type,
|
||||||
|
llvm::Function::LinkageTypes::ExternalLinkage,
|
||||||
|
name + "_supercomb",
|
||||||
|
&module);
|
||||||
|
new_function->arg_begin()->setName("stack");
|
||||||
|
supercombinators[name] = new_function;
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Function* llvm_context::get_current_function() {
|
||||||
|
return current_function;
|
||||||
|
}
|
||||||
|
|
||||||
|
void llvm_context::set_current_function(llvm::Function* f) {
|
||||||
|
current_function = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
61
src/llvm.hpp
Normal file
61
src/llvm.hpp
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
#pragma once
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
#include <llvm/IR/LLVMContext.h>
|
||||||
|
#include <llvm/IR/DerivedTypes.h>
|
||||||
|
#include <llvm/IR/IRBuilder.h>
|
||||||
|
|
||||||
|
namespace lily {
|
||||||
|
extern llvm::LLVMContext context;
|
||||||
|
extern llvm::IRBuilder<> builder;
|
||||||
|
extern llvm::Module module;
|
||||||
|
|
||||||
|
extern llvm::StructType* stack_type;
|
||||||
|
extern llvm::PointerType* stack_pointer_type;
|
||||||
|
|
||||||
|
extern llvm::FunctionType* supercomb_function_type;
|
||||||
|
extern llvm::FunctionType* eval_function_type;
|
||||||
|
extern llvm::PointerType* supercomb_function_pointer_type;
|
||||||
|
|
||||||
|
extern llvm::Function* stack_init_func;
|
||||||
|
extern llvm::Function* stack_free_func;
|
||||||
|
extern llvm::Function* stack_push_func;
|
||||||
|
extern llvm::Function* stack_peek_func;
|
||||||
|
extern llvm::Function* stack_pop_func;
|
||||||
|
extern llvm::Function* stack_popn_func;
|
||||||
|
extern llvm::Function* stack_update_func;
|
||||||
|
extern llvm::Function* stack_alloc_func;
|
||||||
|
extern llvm::Function* stack_slide_func;
|
||||||
|
extern llvm::Function* stack_size_func;
|
||||||
|
|
||||||
|
extern llvm::Function* malloc_node_num_func;
|
||||||
|
extern llvm::Function* malloc_node_app_func;
|
||||||
|
extern llvm::Function* malloc_node_global_func;
|
||||||
|
extern llvm::Function* malloc_node_ind_func;
|
||||||
|
|
||||||
|
extern llvm::IntegerType* tag_type;
|
||||||
|
extern llvm::PointerType* node_pointer_type;
|
||||||
|
extern llvm::StructType* node_parent_type;
|
||||||
|
extern llvm::StructType* node_num_type;
|
||||||
|
extern llvm::StructType* node_app_type;
|
||||||
|
extern llvm::StructType* node_global_type;
|
||||||
|
extern llvm::StructType* node_indirect_type;
|
||||||
|
|
||||||
|
llvm::Value* get_int32_constant(int value);
|
||||||
|
llvm::Value* get_int64_constant(int value);
|
||||||
|
llvm::Value* get_int8_constant(char value);
|
||||||
|
|
||||||
|
void llvm_init();
|
||||||
|
void llvm_generate(const std::string& filename);
|
||||||
|
|
||||||
|
class llvm_context {
|
||||||
|
private:
|
||||||
|
std::map<std::string, llvm::Function*> supercombinators;
|
||||||
|
llvm::Function* current_function;
|
||||||
|
public:
|
||||||
|
llvm::Function* get_supercombinator(const std::string& name);
|
||||||
|
void add_supercombinator(const std::string& name);
|
||||||
|
llvm::Function* get_current_function();
|
||||||
|
void set_current_function(llvm::Function* f);
|
||||||
|
};
|
||||||
|
}
|
15
src/main.cpp
15
src/main.cpp
|
@ -2,22 +2,15 @@
|
||||||
#include "pattern.hpp"
|
#include "pattern.hpp"
|
||||||
#include "parser.hpp"
|
#include "parser.hpp"
|
||||||
#include "gmachine.hpp"
|
#include "gmachine.hpp"
|
||||||
|
#include "llvm.hpp"
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
lily::program_ptr prog = lily::parse(
|
lily::program_ptr prog = lily::parse(
|
||||||
"defn other x y = { 3 }\n"
|
"defn main = { 163 * 2 }"
|
||||||
"defn otherr x y = { let sum = { x + y } in { sum + sum } }\n"
|
|
||||||
);
|
);
|
||||||
std::map<std::string, std::vector<lily::instruction*>> into;
|
prog->gen_llvm();
|
||||||
lily::instruction_manager mgr;
|
|
||||||
prog->compile(mgr, into);
|
|
||||||
for(auto& pair : into) {
|
|
||||||
std::cout << pair.first << std::endl;
|
|
||||||
for(auto& op : pair.second) {
|
|
||||||
std::cout << " " << *op << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch(lily::error& e) {
|
} catch(lily::error& e) {
|
||||||
std::cout << e.message << std::endl;
|
std::cout << e.message << std::endl;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ extern "C" {
|
||||||
#include "parser.hpp"
|
#include "parser.hpp"
|
||||||
#include "pattern.hpp"
|
#include "pattern.hpp"
|
||||||
#include "type_checker.hpp"
|
#include "type_checker.hpp"
|
||||||
|
#include "llvm.hpp"
|
||||||
|
#include "llvm/IR/Verifier.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace lily {
|
namespace lily {
|
||||||
|
@ -294,14 +296,13 @@ namespace lily {
|
||||||
template <binop o>
|
template <binop o>
|
||||||
static void generate_binop(instruction_manager& mgr, std::map<std::string, std::vector<instruction*>>& into) {
|
static void generate_binop(instruction_manager& mgr, std::map<std::string, std::vector<instruction*>>& into) {
|
||||||
std::vector<instruction*> dest;
|
std::vector<instruction*> dest;
|
||||||
dest.push_back(mgr.add_instruction<instruction_push>(1));
|
dest.push_back(mgr.add_instruction<instruction_push>(2));
|
||||||
dest.push_back(mgr.add_instruction<instruction_eval>());
|
dest.push_back(mgr.add_instruction<instruction_eval>());
|
||||||
dest.push_back(mgr.add_instruction<instruction_push>(1));
|
dest.push_back(mgr.add_instruction<instruction_push>(2));
|
||||||
dest.push_back(mgr.add_instruction<instruction_eval>());
|
dest.push_back(mgr.add_instruction<instruction_eval>());
|
||||||
dest.push_back(mgr.add_instruction<instruction_op>(o));
|
dest.push_back(mgr.add_instruction<instruction_op>(o));
|
||||||
dest.push_back(mgr.add_instruction<instruction_update>(2));
|
dest.push_back(mgr.add_instruction<instruction_update>(2));
|
||||||
dest.push_back(mgr.add_instruction<instruction_pop>(2));
|
dest.push_back(mgr.add_instruction<instruction_pop>(2));
|
||||||
dest.push_back(mgr.add_instruction<instruction_unwind>());
|
|
||||||
into[op_supercombinator(o)] = std::move(dest);
|
into[op_supercombinator(o)] = std::move(dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,4 +335,34 @@ namespace lily {
|
||||||
into[pair.first] = std::move(destination);
|
into[pair.first] = std::move(destination);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void program::gen_llvm() {
|
||||||
|
llvm_init();
|
||||||
|
llvm_context ctx;
|
||||||
|
instruction_manager mgr;
|
||||||
|
std::map<std::string, std::vector<instruction*>> gcode;
|
||||||
|
|
||||||
|
compile(mgr, gcode);
|
||||||
|
|
||||||
|
for(auto& pair : gcode) {
|
||||||
|
ctx.add_supercombinator(pair.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto& pair : gcode) {
|
||||||
|
std::vector<instruction*>& comb_gcode = pair.second;
|
||||||
|
llvm::Function* current_function = ctx.get_supercombinator(pair.first);
|
||||||
|
ctx.set_current_function(current_function);
|
||||||
|
llvm::BasicBlock* new_block =
|
||||||
|
llvm::BasicBlock::Create(context, "entry", current_function);
|
||||||
|
builder.SetInsertPoint(new_block);
|
||||||
|
for(auto& op : comb_gcode) {
|
||||||
|
op->gen_llvm(ctx);
|
||||||
|
}
|
||||||
|
builder.CreateRetVoid();
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::verifyModule(module, &llvm::outs());
|
||||||
|
llvm_generate("lily.o");
|
||||||
|
module.print(llvm::outs(), NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ namespace lily {
|
||||||
|
|
||||||
void check();
|
void check();
|
||||||
void compile(instruction_manager& mgr, std::map<std::string, std::vector<instruction*>>& into);
|
void compile(instruction_manager& mgr, std::map<std::string, std::vector<instruction*>>& into);
|
||||||
|
void gen_llvm();
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::unique_ptr<program> program_ptr;
|
typedef std::unique_ptr<program> program_ptr;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user