Compare commits
No commits in common. "1a8a1c30521b7245301856a465ec7e108000ff60" and "c79b5a4120e0d185a83f994971c74f8579b6969d" have entirely different histories.
1a8a1c3052
...
c79b5a4120
@ -104,7 +104,7 @@ void ast_binop::compile(const env_ptr& env, std::vector<instruction_ptr>& into)
|
|||||||
right->compile(env, into);
|
right->compile(env, into);
|
||||||
left->compile(env_ptr(new env_offset(1, env)), into);
|
left->compile(env_ptr(new env_offset(1, env)), into);
|
||||||
|
|
||||||
into.push_back(instruction_ptr(new instruction_pushglobal(op_action(op))));
|
into.push_back(instruction_ptr(new instruction_pushglobal(op_name(op))));
|
||||||
into.push_back(instruction_ptr(new instruction_mkapp()));
|
into.push_back(instruction_ptr(new instruction_mkapp()));
|
||||||
into.push_back(instruction_ptr(new instruction_mkapp()));
|
into.push_back(instruction_ptr(new instruction_mkapp()));
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,6 @@ using branch_ptr = std::unique_ptr<branch>;
|
|||||||
struct constructor {
|
struct constructor {
|
||||||
std::string name;
|
std::string name;
|
||||||
std::vector<std::string> types;
|
std::vector<std::string> types;
|
||||||
int8_t tag;
|
|
||||||
|
|
||||||
constructor(std::string n, std::vector<std::string> ts)
|
constructor(std::string n, std::vector<std::string> ts)
|
||||||
: name(std::move(n)), types(std::move(ts)) {}
|
: name(std::move(n)), types(std::move(ts)) {}
|
||||||
|
@ -48,7 +48,6 @@ void definition_defn::compile() {
|
|||||||
}
|
}
|
||||||
body->compile(new_env, instructions);
|
body->compile(new_env, instructions);
|
||||||
instructions.push_back(instruction_ptr(new instruction_update(params.size())));
|
instructions.push_back(instruction_ptr(new instruction_update(params.size())));
|
||||||
instructions.push_back(instruction_ptr(new instruction_pop(params.size())));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void definition_data::typecheck_first(type_mgr& mgr, type_env& env) {
|
void definition_data::typecheck_first(type_mgr& mgr, type_env& env) {
|
||||||
@ -57,7 +56,6 @@ void definition_data::typecheck_first(type_mgr& mgr, type_env& env) {
|
|||||||
int next_tag = 0;
|
int next_tag = 0;
|
||||||
|
|
||||||
for(auto& constructor : constructors) {
|
for(auto& constructor : constructors) {
|
||||||
constructor->tag = next_tag;
|
|
||||||
this_type->constructors[constructor->name] = { next_tag++ };
|
this_type->constructors[constructor->name] = { next_tag++ };
|
||||||
|
|
||||||
type_ptr full_type = return_type;
|
type_ptr full_type = return_type;
|
||||||
|
@ -19,11 +19,6 @@ void instruction_push::print(int indent, std::ostream& to) const {
|
|||||||
to << "Push(" << offset << ")" << std::endl;
|
to << "Push(" << offset << ")" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void instruction_pop::print(int indent, std::ostream& to) const {
|
|
||||||
print_indent(indent, to);
|
|
||||||
to << "Pop(" << count << ")" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void instruction_mkapp::print(int indent, std::ostream& to) const {
|
void instruction_mkapp::print(int indent, std::ostream& to) const {
|
||||||
print_indent(indent, to);
|
print_indent(indent, to);
|
||||||
to << "MkApp()" << std::endl;
|
to << "MkApp()" << std::endl;
|
||||||
|
@ -41,15 +41,6 @@ struct instruction_push : public instruction {
|
|||||||
void print(int indent, std::ostream& to) const;
|
void print(int indent, std::ostream& to) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct instruction_pop : public instruction {
|
|
||||||
int count;
|
|
||||||
|
|
||||||
instruction_pop(int c)
|
|
||||||
: count(c) {}
|
|
||||||
|
|
||||||
void print(int indent, std::ostream& to) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct instruction_mkapp : public instruction {
|
struct instruction_mkapp : public instruction {
|
||||||
void print(int indent, std::ostream& to) const;
|
void print(int indent, std::ostream& to) const;
|
||||||
};
|
};
|
||||||
|
@ -104,7 +104,7 @@ void ast_binop::compile(const env_ptr& env, std::vector<instruction_ptr>& into)
|
|||||||
right->compile(env, into);
|
right->compile(env, into);
|
||||||
left->compile(env_ptr(new env_offset(1, env)), into);
|
left->compile(env_ptr(new env_offset(1, env)), into);
|
||||||
|
|
||||||
into.push_back(instruction_ptr(new instruction_pushglobal(op_action(op))));
|
into.push_back(instruction_ptr(new instruction_pushglobal(op_name(op))));
|
||||||
into.push_back(instruction_ptr(new instruction_mkapp()));
|
into.push_back(instruction_ptr(new instruction_mkapp()));
|
||||||
into.push_back(instruction_ptr(new instruction_mkapp()));
|
into.push_back(instruction_ptr(new instruction_mkapp()));
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,6 @@ using branch_ptr = std::unique_ptr<branch>;
|
|||||||
struct constructor {
|
struct constructor {
|
||||||
std::string name;
|
std::string name;
|
||||||
std::vector<std::string> types;
|
std::vector<std::string> types;
|
||||||
int8_t tag;
|
|
||||||
|
|
||||||
constructor(std::string n, std::vector<std::string> ts)
|
constructor(std::string n, std::vector<std::string> ts)
|
||||||
: name(std::move(n)), types(std::move(ts)) {}
|
: name(std::move(n)), types(std::move(ts)) {}
|
||||||
|
@ -48,7 +48,6 @@ void definition_defn::compile() {
|
|||||||
}
|
}
|
||||||
body->compile(new_env, instructions);
|
body->compile(new_env, instructions);
|
||||||
instructions.push_back(instruction_ptr(new instruction_update(params.size())));
|
instructions.push_back(instruction_ptr(new instruction_update(params.size())));
|
||||||
instructions.push_back(instruction_ptr(new instruction_pop(params.size())));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void definition_data::typecheck_first(type_mgr& mgr, type_env& env) {
|
void definition_data::typecheck_first(type_mgr& mgr, type_env& env) {
|
||||||
@ -57,7 +56,6 @@ void definition_data::typecheck_first(type_mgr& mgr, type_env& env) {
|
|||||||
int next_tag = 0;
|
int next_tag = 0;
|
||||||
|
|
||||||
for(auto& constructor : constructors) {
|
for(auto& constructor : constructors) {
|
||||||
constructor->tag = next_tag;
|
|
||||||
this_type->constructors[constructor->name] = { next_tag++ };
|
this_type->constructors[constructor->name] = { next_tag++ };
|
||||||
|
|
||||||
type_ptr full_type = return_type;
|
type_ptr full_type = return_type;
|
||||||
|
@ -19,11 +19,6 @@ void instruction_push::print(int indent, std::ostream& to) const {
|
|||||||
to << "Push(" << offset << ")" << std::endl;
|
to << "Push(" << offset << ")" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void instruction_pop::print(int indent, std::ostream& to) const {
|
|
||||||
print_indent(indent, to);
|
|
||||||
to << "Pop(" << count << ")" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void instruction_mkapp::print(int indent, std::ostream& to) const {
|
void instruction_mkapp::print(int indent, std::ostream& to) const {
|
||||||
print_indent(indent, to);
|
print_indent(indent, to);
|
||||||
to << "MkApp()" << std::endl;
|
to << "MkApp()" << std::endl;
|
||||||
|
@ -41,15 +41,6 @@ struct instruction_push : public instruction {
|
|||||||
void print(int indent, std::ostream& to) const;
|
void print(int indent, std::ostream& to) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct instruction_pop : public instruction {
|
|
||||||
int count;
|
|
||||||
|
|
||||||
instruction_pop(int c)
|
|
||||||
: count(c) {}
|
|
||||||
|
|
||||||
void print(int indent, std::ostream& to) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct instruction_mkapp : public instruction {
|
struct instruction_mkapp : public instruction {
|
||||||
void print(int indent, std::ostream& to) const;
|
void print(int indent, std::ostream& to) const;
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#include "ast.hpp"
|
#include "ast.hpp"
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include "binop.hpp"
|
|
||||||
#include "error.hpp"
|
#include "error.hpp"
|
||||||
|
|
||||||
static void print_indent(int n, std::ostream& to) {
|
static void print_indent(int n, std::ostream& to) {
|
||||||
@ -105,7 +104,7 @@ void ast_binop::compile(const env_ptr& env, std::vector<instruction_ptr>& into)
|
|||||||
right->compile(env, into);
|
right->compile(env, into);
|
||||||
left->compile(env_ptr(new env_offset(1, env)), into);
|
left->compile(env_ptr(new env_offset(1, env)), into);
|
||||||
|
|
||||||
into.push_back(instruction_ptr(new instruction_pushglobal(op_action(op))));
|
into.push_back(instruction_ptr(new instruction_pushglobal(op_name(op))));
|
||||||
into.push_back(instruction_ptr(new instruction_mkapp()));
|
into.push_back(instruction_ptr(new instruction_mkapp()));
|
||||||
into.push_back(instruction_ptr(new instruction_mkapp()));
|
into.push_back(instruction_ptr(new instruction_mkapp()));
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,27 @@ struct branch {
|
|||||||
|
|
||||||
using branch_ptr = std::unique_ptr<branch>;
|
using branch_ptr = std::unique_ptr<branch>;
|
||||||
|
|
||||||
|
struct constructor {
|
||||||
|
std::string name;
|
||||||
|
std::vector<std::string> types;
|
||||||
|
|
||||||
|
constructor(std::string n, std::vector<std::string> ts)
|
||||||
|
: name(std::move(n)), types(std::move(ts)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
using constructor_ptr = std::unique_ptr<constructor>;
|
||||||
|
|
||||||
|
struct definition {
|
||||||
|
virtual ~definition() = default;
|
||||||
|
|
||||||
|
virtual void typecheck_first(type_mgr& mgr, type_env& env) = 0;
|
||||||
|
virtual void typecheck_second(type_mgr& mgr, const type_env& env) const = 0;
|
||||||
|
virtual void resolve(const type_mgr& mgr) = 0;
|
||||||
|
virtual void compile() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
using definition_ptr = std::unique_ptr<definition>;
|
||||||
|
|
||||||
struct ast_int : public ast {
|
struct ast_int : public ast {
|
||||||
int value;
|
int value;
|
||||||
|
|
||||||
@ -139,3 +160,37 @@ struct pattern_constr : public pattern {
|
|||||||
void print(std::ostream &to) const;
|
void print(std::ostream &to) const;
|
||||||
void match(type_ptr t, type_mgr&, type_env& env) const;
|
void match(type_ptr t, type_mgr&, type_env& env) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct definition_defn : public definition {
|
||||||
|
std::string name;
|
||||||
|
std::vector<std::string> params;
|
||||||
|
ast_ptr body;
|
||||||
|
|
||||||
|
type_ptr return_type;
|
||||||
|
std::vector<type_ptr> param_types;
|
||||||
|
|
||||||
|
std::vector<instruction_ptr> instructions;
|
||||||
|
|
||||||
|
definition_defn(std::string n, std::vector<std::string> p, ast_ptr b)
|
||||||
|
: name(std::move(n)), params(std::move(p)), body(std::move(b)) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void typecheck_first(type_mgr& mgr, type_env& env);
|
||||||
|
void typecheck_second(type_mgr& mgr, const type_env& env) const;
|
||||||
|
void resolve(const type_mgr& mgr);
|
||||||
|
void compile();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct definition_data : public definition {
|
||||||
|
std::string name;
|
||||||
|
std::vector<constructor_ptr> constructors;
|
||||||
|
|
||||||
|
definition_data(std::string n, std::vector<constructor_ptr> cs)
|
||||||
|
: name(std::move(n)), constructors(std::move(cs)) {}
|
||||||
|
|
||||||
|
void typecheck_first(type_mgr& mgr, type_env& env);
|
||||||
|
void typecheck_second(type_mgr& mgr, const type_env& env) const;
|
||||||
|
void resolve(const type_mgr& mgr);
|
||||||
|
void compile();
|
||||||
|
};
|
||||||
|
@ -1,10 +1,5 @@
|
|||||||
#include "definition.hpp"
|
|
||||||
#include "error.hpp"
|
|
||||||
#include "ast.hpp"
|
#include "ast.hpp"
|
||||||
#include "llvm_context.hpp"
|
#include "error.hpp"
|
||||||
#include <llvm/IR/DerivedTypes.h>
|
|
||||||
#include <llvm/IR/Function.h>
|
|
||||||
#include <llvm/IR/Type.h>
|
|
||||||
|
|
||||||
void definition_defn::typecheck_first(type_mgr& mgr, type_env& env) {
|
void definition_defn::typecheck_first(type_mgr& mgr, type_env& env) {
|
||||||
return_type = mgr.new_type();
|
return_type = mgr.new_type();
|
||||||
@ -53,18 +48,6 @@ void definition_defn::compile() {
|
|||||||
}
|
}
|
||||||
body->compile(new_env, instructions);
|
body->compile(new_env, instructions);
|
||||||
instructions.push_back(instruction_ptr(new instruction_update(params.size())));
|
instructions.push_back(instruction_ptr(new instruction_update(params.size())));
|
||||||
instructions.push_back(instruction_ptr(new instruction_pop(params.size())));
|
|
||||||
}
|
|
||||||
void definition_defn::gen_llvm_first(llvm_context& ctx) {
|
|
||||||
generated_function = ctx.create_custom_function(name, params.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
void definition_defn::gen_llvm_second(llvm_context& ctx) {
|
|
||||||
ctx.builder.SetInsertPoint(&generated_function->getEntryBlock());
|
|
||||||
for(auto& instruction : instructions) {
|
|
||||||
instruction->gen_llvm(ctx, generated_function);
|
|
||||||
}
|
|
||||||
ctx.builder.CreateRetVoid();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void definition_data::typecheck_first(type_mgr& mgr, type_env& env) {
|
void definition_data::typecheck_first(type_mgr& mgr, type_env& env) {
|
||||||
@ -73,7 +56,6 @@ void definition_data::typecheck_first(type_mgr& mgr, type_env& env) {
|
|||||||
int next_tag = 0;
|
int next_tag = 0;
|
||||||
|
|
||||||
for(auto& constructor : constructors) {
|
for(auto& constructor : constructors) {
|
||||||
constructor->tag = next_tag;
|
|
||||||
this_type->constructors[constructor->name] = { next_tag++ };
|
this_type->constructors[constructor->name] = { next_tag++ };
|
||||||
|
|
||||||
type_ptr full_type = return_type;
|
type_ptr full_type = return_type;
|
||||||
@ -97,20 +79,3 @@ void definition_data::resolve(const type_mgr& mgr) {
|
|||||||
void definition_data::compile() {
|
void definition_data::compile() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void definition_data::gen_llvm_first(llvm_context& ctx) {
|
|
||||||
for(auto& constructor : constructors) {
|
|
||||||
auto new_function =
|
|
||||||
ctx.create_custom_function(constructor->name, constructor->types.size());
|
|
||||||
ctx.builder.SetInsertPoint(&new_function->getEntryBlock());
|
|
||||||
ctx.create_pack(new_function,
|
|
||||||
ctx.create_size(constructor->types.size()),
|
|
||||||
ctx.create_i8(constructor->tag)
|
|
||||||
);
|
|
||||||
ctx.builder.CreateRetVoid();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void definition_data::gen_llvm_second(llvm_context& ctx) {
|
|
||||||
// Nothing
|
|
||||||
}
|
|
||||||
|
@ -1,73 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
#include "instruction.hpp"
|
|
||||||
#include "llvm_context.hpp"
|
|
||||||
#include "type_env.hpp"
|
|
||||||
|
|
||||||
struct ast;
|
|
||||||
using ast_ptr = std::unique_ptr<ast>;
|
|
||||||
|
|
||||||
struct definition {
|
|
||||||
virtual ~definition() = default;
|
|
||||||
|
|
||||||
virtual void typecheck_first(type_mgr& mgr, type_env& env) = 0;
|
|
||||||
virtual void typecheck_second(type_mgr& mgr, const type_env& env) const = 0;
|
|
||||||
virtual void resolve(const type_mgr& mgr) = 0;
|
|
||||||
virtual void compile() = 0;
|
|
||||||
virtual void gen_llvm_first(llvm_context& ctx) = 0;
|
|
||||||
virtual void gen_llvm_second(llvm_context& ctx) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
using definition_ptr = std::unique_ptr<definition>;
|
|
||||||
|
|
||||||
struct constructor {
|
|
||||||
std::string name;
|
|
||||||
std::vector<std::string> types;
|
|
||||||
int8_t tag;
|
|
||||||
|
|
||||||
constructor(std::string n, std::vector<std::string> ts)
|
|
||||||
: name(std::move(n)), types(std::move(ts)) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
using constructor_ptr = std::unique_ptr<constructor>;
|
|
||||||
|
|
||||||
struct definition_defn : public definition {
|
|
||||||
std::string name;
|
|
||||||
std::vector<std::string> params;
|
|
||||||
ast_ptr body;
|
|
||||||
|
|
||||||
type_ptr return_type;
|
|
||||||
std::vector<type_ptr> param_types;
|
|
||||||
|
|
||||||
std::vector<instruction_ptr> instructions;
|
|
||||||
|
|
||||||
llvm::Function* generated_function;
|
|
||||||
|
|
||||||
definition_defn(std::string n, std::vector<std::string> p, ast_ptr b)
|
|
||||||
: name(std::move(n)), params(std::move(p)), body(std::move(b)) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void typecheck_first(type_mgr& mgr, type_env& env);
|
|
||||||
void typecheck_second(type_mgr& mgr, const type_env& env) const;
|
|
||||||
void resolve(const type_mgr& mgr);
|
|
||||||
void compile();
|
|
||||||
void gen_llvm_first(llvm_context& ctx);
|
|
||||||
void gen_llvm_second(llvm_context& ctx);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct definition_data : public definition {
|
|
||||||
std::string name;
|
|
||||||
std::vector<constructor_ptr> constructors;
|
|
||||||
|
|
||||||
definition_data(std::string n, std::vector<constructor_ptr> cs)
|
|
||||||
: name(std::move(n)), constructors(std::move(cs)) {}
|
|
||||||
|
|
||||||
void typecheck_first(type_mgr& mgr, type_env& env);
|
|
||||||
void typecheck_second(type_mgr& mgr, const type_env& env) const;
|
|
||||||
void resolve(const type_mgr& mgr);
|
|
||||||
void compile();
|
|
||||||
void gen_llvm_first(llvm_context& ctx);
|
|
||||||
void gen_llvm_second(llvm_context& ctx);
|
|
||||||
};
|
|
@ -1,6 +1,5 @@
|
|||||||
#include "instruction.hpp"
|
#include "instruction.hpp"
|
||||||
#include "llvm_context.hpp"
|
#include "llvm_context.hpp"
|
||||||
#include <llvm/IR/BasicBlock.h>
|
|
||||||
#include <llvm/IR/Function.h>
|
#include <llvm/IR/Function.h>
|
||||||
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
@ -24,9 +23,7 @@ void instruction_pushglobal::print(int indent, std::ostream& to) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void instruction_pushglobal::gen_llvm(llvm_context& ctx, Function* f) const {
|
void instruction_pushglobal::gen_llvm(llvm_context& ctx, Function* f) const {
|
||||||
auto& global_f = ctx.custom_functions.at("f_" + name);
|
// TODO
|
||||||
auto arity = ctx.create_i32(global_f->arity);
|
|
||||||
ctx.create_push(f, ctx.create_global(global_f->function, arity));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void instruction_push::print(int indent, std::ostream& to) const {
|
void instruction_push::print(int indent, std::ostream& to) const {
|
||||||
@ -38,15 +35,6 @@ void instruction_push::gen_llvm(llvm_context& ctx, Function* f) const {
|
|||||||
ctx.create_push(f, ctx.create_peek(f, ctx.create_size(offset)));
|
ctx.create_push(f, ctx.create_peek(f, ctx.create_size(offset)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void instruction_pop::print(int indent, std::ostream& to) const {
|
|
||||||
print_indent(indent, to);
|
|
||||||
to << "Pop(" << count << ")" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void instruction_pop::gen_llvm(llvm_context& ctx, Function* f) const {
|
|
||||||
ctx.create_popn(f, ctx.create_size(count));
|
|
||||||
}
|
|
||||||
|
|
||||||
void instruction_mkapp::print(int indent, std::ostream& to) const {
|
void instruction_mkapp::print(int indent, std::ostream& to) const {
|
||||||
print_indent(indent, to);
|
print_indent(indent, to);
|
||||||
to << "MkApp()" << std::endl;
|
to << "MkApp()" << std::endl;
|
||||||
@ -99,27 +87,7 @@ void instruction_jump::print(int indent, std::ostream& to) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void instruction_jump::gen_llvm(llvm_context& ctx, Function* f) const {
|
void instruction_jump::gen_llvm(llvm_context& ctx, Function* f) const {
|
||||||
auto top_node = ctx.create_peek(f, ctx.create_size(0));
|
// TODO
|
||||||
auto tag = ctx.unwrap_data_tag(top_node);
|
|
||||||
auto safety_block = BasicBlock::Create(ctx.ctx, "safety", f);
|
|
||||||
auto switch_op = ctx.builder.CreateSwitch(tag, safety_block, tag_mappings.size());
|
|
||||||
std::vector<BasicBlock*> blocks;
|
|
||||||
|
|
||||||
for(auto& branch : branches) {
|
|
||||||
auto branch_block = BasicBlock::Create(ctx.ctx, "branch", f);
|
|
||||||
ctx.builder.SetInsertPoint(branch_block);
|
|
||||||
for(auto& instruction : branch) {
|
|
||||||
instruction->gen_llvm(ctx, f);
|
|
||||||
}
|
|
||||||
ctx.builder.CreateBr(safety_block);
|
|
||||||
blocks.push_back(branch_block);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(auto& mapping : tag_mappings) {
|
|
||||||
switch_op->addCase(ctx.create_i8(mapping.first), blocks[mapping.second]);
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.builder.SetInsertPoint(safety_block);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void instruction_slide::print(int indent, std::ostream& to) const {
|
void instruction_slide::print(int indent, std::ostream& to) const {
|
||||||
|
@ -47,16 +47,6 @@ struct instruction_push : public instruction {
|
|||||||
void gen_llvm(llvm_context& ctx, llvm::Function* f) const;
|
void gen_llvm(llvm_context& ctx, llvm::Function* f) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct instruction_pop : public instruction {
|
|
||||||
int count;
|
|
||||||
|
|
||||||
instruction_pop(int c)
|
|
||||||
: count(c) {}
|
|
||||||
|
|
||||||
void print(int indent, std::ostream& to) const;
|
|
||||||
void gen_llvm(llvm_context& ctx, llvm::Function* f) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct instruction_mkapp : public instruction {
|
struct instruction_mkapp : public instruction {
|
||||||
void print(int indent, std::ostream& to) const;
|
void print(int indent, std::ostream& to) const;
|
||||||
void gen_llvm(llvm_context& ctx, llvm::Function* f) const;
|
void gen_llvm(llvm_context& ctx, llvm::Function* f) const;
|
||||||
|
@ -67,49 +67,49 @@ void llvm_context::create_functions() {
|
|||||||
functions["stack_pop"] = Function::Create(
|
functions["stack_pop"] = Function::Create(
|
||||||
FunctionType::get(node_ptr_type, { stack_ptr_type }, false),
|
FunctionType::get(node_ptr_type, { stack_ptr_type }, false),
|
||||||
Function::LinkageTypes::ExternalLinkage,
|
Function::LinkageTypes::ExternalLinkage,
|
||||||
"stack_pop",
|
"stack_push",
|
||||||
&module
|
&module
|
||||||
);
|
);
|
||||||
functions["stack_peek"] = Function::Create(
|
functions["stack_peek"] = Function::Create(
|
||||||
FunctionType::get(node_ptr_type, { stack_ptr_type, sizet_type }, false),
|
FunctionType::get(node_ptr_type, { stack_ptr_type, sizet_type }, false),
|
||||||
Function::LinkageTypes::ExternalLinkage,
|
Function::LinkageTypes::ExternalLinkage,
|
||||||
"stack_peek",
|
"stack_push",
|
||||||
&module
|
&module
|
||||||
);
|
);
|
||||||
functions["stack_popn"] = Function::Create(
|
functions["stack_popn"] = Function::Create(
|
||||||
FunctionType::get(void_type, { stack_ptr_type, sizet_type }, false),
|
FunctionType::get(void_type, { stack_ptr_type, sizet_type }, false),
|
||||||
Function::LinkageTypes::ExternalLinkage,
|
Function::LinkageTypes::ExternalLinkage,
|
||||||
"stack_popn",
|
"stack_push",
|
||||||
&module
|
&module
|
||||||
);
|
);
|
||||||
functions["stack_slide"] = Function::Create(
|
functions["stack_slide"] = Function::Create(
|
||||||
FunctionType::get(void_type, { stack_ptr_type, sizet_type }, false),
|
FunctionType::get(void_type, { stack_ptr_type, sizet_type }, false),
|
||||||
Function::LinkageTypes::ExternalLinkage,
|
Function::LinkageTypes::ExternalLinkage,
|
||||||
"stack_slide",
|
"stack_push",
|
||||||
&module
|
&module
|
||||||
);
|
);
|
||||||
functions["stack_update"] = Function::Create(
|
functions["stack_update"] = Function::Create(
|
||||||
FunctionType::get(void_type, { stack_ptr_type, sizet_type }, false),
|
FunctionType::get(void_type, { stack_ptr_type, sizet_type }, false),
|
||||||
Function::LinkageTypes::ExternalLinkage,
|
Function::LinkageTypes::ExternalLinkage,
|
||||||
"stack_update",
|
"stack_push",
|
||||||
&module
|
&module
|
||||||
);
|
);
|
||||||
functions["stack_alloc"] = Function::Create(
|
functions["stack_alloc"] = Function::Create(
|
||||||
FunctionType::get(void_type, { stack_ptr_type, sizet_type }, false),
|
FunctionType::get(void_type, { stack_ptr_type, sizet_type }, false),
|
||||||
Function::LinkageTypes::ExternalLinkage,
|
Function::LinkageTypes::ExternalLinkage,
|
||||||
"stack_alloc",
|
"stack_push",
|
||||||
&module
|
&module
|
||||||
);
|
);
|
||||||
functions["stack_pack"] = Function::Create(
|
functions["stack_pack"] = Function::Create(
|
||||||
FunctionType::get(void_type, { stack_ptr_type, sizet_type, tag_type }, false),
|
FunctionType::get(void_type, { stack_ptr_type, sizet_type, tag_type }, false),
|
||||||
Function::LinkageTypes::ExternalLinkage,
|
Function::LinkageTypes::ExternalLinkage,
|
||||||
"stack_pack",
|
"stack_push",
|
||||||
&module
|
&module
|
||||||
);
|
);
|
||||||
functions["stack_split"] = Function::Create(
|
functions["stack_split"] = Function::Create(
|
||||||
FunctionType::get(node_ptr_type, { stack_ptr_type, sizet_type }, false),
|
FunctionType::get(node_ptr_type, { stack_ptr_type, sizet_type }, false),
|
||||||
Function::LinkageTypes::ExternalLinkage,
|
Function::LinkageTypes::ExternalLinkage,
|
||||||
"stack_split",
|
"stack_push",
|
||||||
&module
|
&module
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -147,13 +147,13 @@ void llvm_context::create_functions() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConstantInt* llvm_context::create_i8(int8_t i) {
|
Value* llvm_context::create_i8(int8_t i) {
|
||||||
return ConstantInt::get(ctx, APInt(8, i));
|
return ConstantInt::get(ctx, APInt(8, i));
|
||||||
}
|
}
|
||||||
ConstantInt* llvm_context::create_i32(int32_t i) {
|
Value* llvm_context::create_i32(int32_t i) {
|
||||||
return ConstantInt::get(ctx, APInt(32, i));
|
return ConstantInt::get(ctx, APInt(32, i));
|
||||||
}
|
}
|
||||||
ConstantInt* llvm_context::create_size(size_t i) {
|
Value* llvm_context::create_size(size_t i) {
|
||||||
return ConstantInt::get(ctx, APInt(sizeof(size_t) * 8, i));
|
return ConstantInt::get(ctx, APInt(sizeof(size_t) * 8, i));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,8 +202,8 @@ Value* llvm_context::create_eval(Value* e) {
|
|||||||
Value* llvm_context::unwrap_num(Value* v) {
|
Value* llvm_context::unwrap_num(Value* v) {
|
||||||
auto num_ptr_type = PointerType::getUnqual(struct_types.at("node_num"));
|
auto num_ptr_type = PointerType::getUnqual(struct_types.at("node_num"));
|
||||||
auto cast = builder.CreatePointerCast(v, num_ptr_type);
|
auto cast = builder.CreatePointerCast(v, num_ptr_type);
|
||||||
auto offset_0 = create_i32(0);
|
auto offset_0 = create_size(0);
|
||||||
auto offset_1 = create_i32(1);
|
auto offset_1 = create_size(1);
|
||||||
auto int_ptr = builder.CreateGEP(cast, { offset_0, offset_1 });
|
auto int_ptr = builder.CreateGEP(cast, { offset_0, offset_1 });
|
||||||
return builder.CreateLoad(int_ptr);
|
return builder.CreateLoad(int_ptr);
|
||||||
}
|
}
|
||||||
@ -212,15 +212,6 @@ Value* llvm_context::create_num(Value* v) {
|
|||||||
return builder.CreateCall(alloc_num_f, { v });
|
return builder.CreateCall(alloc_num_f, { v });
|
||||||
}
|
}
|
||||||
|
|
||||||
Value* llvm_context::unwrap_data_tag(Value* v) {
|
|
||||||
auto data_ptr_type = PointerType::getUnqual(struct_types.at("node_data"));
|
|
||||||
auto cast = builder.CreatePointerCast(v, data_ptr_type);
|
|
||||||
auto offset_0 = create_i32(0);
|
|
||||||
auto offset_1 = create_i32(1);
|
|
||||||
auto tag_ptr = builder.CreateGEP(cast, { offset_0, offset_1 });
|
|
||||||
return builder.CreateLoad(tag_ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
Value* llvm_context::create_global(Value* f, Value* a) {
|
Value* llvm_context::create_global(Value* f, Value* a) {
|
||||||
auto alloc_global_f = functions.at("alloc_global");
|
auto alloc_global_f = functions.at("alloc_global");
|
||||||
return builder.CreateCall(alloc_global_f, { f, a });
|
return builder.CreateCall(alloc_global_f, { f, a });
|
||||||
@ -230,23 +221,3 @@ Value* llvm_context::create_app(Value* l, Value* r) {
|
|||||||
auto alloc_app_f = functions.at("alloc_app");
|
auto alloc_app_f = functions.at("alloc_app");
|
||||||
return builder.CreateCall(alloc_app_f, { l, r });
|
return builder.CreateCall(alloc_app_f, { l, r });
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::Function* llvm_context::create_custom_function(std::string name, int32_t arity) {
|
|
||||||
auto void_type = llvm::Type::getVoidTy(ctx);
|
|
||||||
auto function_type =
|
|
||||||
llvm::FunctionType::get(void_type, { stack_ptr_type }, false);
|
|
||||||
auto new_function = llvm::Function::Create(
|
|
||||||
function_type,
|
|
||||||
llvm::Function::LinkageTypes::ExternalLinkage,
|
|
||||||
"f_" + name,
|
|
||||||
&module
|
|
||||||
);
|
|
||||||
auto start_block = llvm::BasicBlock::Create(ctx, "entry", new_function);
|
|
||||||
|
|
||||||
auto new_custom_f = custom_function_ptr(new custom_function());
|
|
||||||
new_custom_f->arity = arity;
|
|
||||||
new_custom_f->function = new_function;
|
|
||||||
custom_functions["f_" + name] = std::move(new_custom_f);
|
|
||||||
|
|
||||||
return new_function;
|
|
||||||
}
|
|
||||||
|
@ -7,18 +7,10 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
struct llvm_context {
|
struct llvm_context {
|
||||||
struct custom_function {
|
|
||||||
llvm::Function* function;
|
|
||||||
int32_t arity;
|
|
||||||
};
|
|
||||||
|
|
||||||
using custom_function_ptr = std::unique_ptr<custom_function>;
|
|
||||||
|
|
||||||
llvm::LLVMContext ctx;
|
llvm::LLVMContext ctx;
|
||||||
llvm::IRBuilder<> builder;
|
llvm::IRBuilder<> builder;
|
||||||
llvm::Module module;
|
llvm::Module module;
|
||||||
|
|
||||||
std::map<std::string, custom_function_ptr> custom_functions;
|
|
||||||
std::map<std::string, llvm::Function*> functions;
|
std::map<std::string, llvm::Function*> functions;
|
||||||
std::map<std::string, llvm::StructType*> struct_types;
|
std::map<std::string, llvm::StructType*> struct_types;
|
||||||
|
|
||||||
@ -37,9 +29,9 @@ struct llvm_context {
|
|||||||
void create_types();
|
void create_types();
|
||||||
void create_functions();
|
void create_functions();
|
||||||
|
|
||||||
llvm::ConstantInt* create_i8(int8_t);
|
llvm::Value* create_i8(int8_t);
|
||||||
llvm::ConstantInt* create_i32(int32_t);
|
llvm::Value* create_i32(int32_t);
|
||||||
llvm::ConstantInt* create_size(size_t);
|
llvm::Value* create_size(size_t);
|
||||||
|
|
||||||
llvm::Value* create_pop(llvm::Function*);
|
llvm::Value* create_pop(llvm::Function*);
|
||||||
llvm::Value* create_peek(llvm::Function*, llvm::Value*);
|
llvm::Value* create_peek(llvm::Function*, llvm::Value*);
|
||||||
@ -56,11 +48,7 @@ struct llvm_context {
|
|||||||
llvm::Value* unwrap_num(llvm::Value*);
|
llvm::Value* unwrap_num(llvm::Value*);
|
||||||
llvm::Value* create_num(llvm::Value*);
|
llvm::Value* create_num(llvm::Value*);
|
||||||
|
|
||||||
llvm::Value* unwrap_data_tag(llvm::Value*);
|
|
||||||
|
|
||||||
llvm::Value* create_global(llvm::Value*, llvm::Value*);
|
llvm::Value* create_global(llvm::Value*, llvm::Value*);
|
||||||
|
|
||||||
llvm::Value* create_app(llvm::Value*, llvm::Value*);
|
llvm::Value* create_app(llvm::Value*, llvm::Value*);
|
||||||
|
|
||||||
llvm::Function* create_custom_function(std::string name, int32_t arity);
|
|
||||||
};
|
};
|
||||||
|
@ -1,20 +1,8 @@
|
|||||||
#include "ast.hpp"
|
#include "ast.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "binop.hpp"
|
|
||||||
#include "definition.hpp"
|
|
||||||
#include "instruction.hpp"
|
|
||||||
#include "llvm_context.hpp"
|
|
||||||
#include "parser.hpp"
|
#include "parser.hpp"
|
||||||
#include "error.hpp"
|
#include "error.hpp"
|
||||||
#include "type.hpp"
|
#include "type.hpp"
|
||||||
#include "llvm/IR/LegacyPassManager.h"
|
|
||||||
#include "llvm/IR/Verifier.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"
|
|
||||||
|
|
||||||
void yy::parser::error(const std::string& msg) {
|
void yy::parser::error(const std::string& msg) {
|
||||||
std::cout << "An error occured: " << msg << std::endl;
|
std::cout << "An error occured: " << msg << std::endl;
|
||||||
@ -67,80 +55,6 @@ void compile_program(const std::vector<definition_ptr>& prog) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void gen_llvm_internal_op(llvm_context& ctx, binop op) {
|
|
||||||
auto new_function = ctx.create_custom_function(op_action(op), 2);
|
|
||||||
std::vector<instruction_ptr> instructions;
|
|
||||||
instructions.push_back(instruction_ptr(new instruction_push(1)));
|
|
||||||
instructions.push_back(instruction_ptr(new instruction_eval()));
|
|
||||||
instructions.push_back(instruction_ptr(new instruction_push(1)));
|
|
||||||
instructions.push_back(instruction_ptr(new instruction_eval()));
|
|
||||||
instructions.push_back(instruction_ptr(new instruction_binop(op)));
|
|
||||||
ctx.builder.SetInsertPoint(&new_function->getEntryBlock());
|
|
||||||
for(auto& instruction : instructions) {
|
|
||||||
instruction->gen_llvm(ctx, new_function);
|
|
||||||
}
|
|
||||||
ctx.builder.CreateRetVoid();
|
|
||||||
}
|
|
||||||
|
|
||||||
void output_llvm(llvm_context& ctx, 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>());
|
|
||||||
|
|
||||||
ctx.module.setDataLayout(targetMachine->createDataLayout());
|
|
||||||
ctx.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(ctx.module);
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void gen_llvm(const std::vector<definition_ptr>& prog) {
|
|
||||||
llvm_context ctx;
|
|
||||||
gen_llvm_internal_op(ctx, PLUS);
|
|
||||||
gen_llvm_internal_op(ctx, MINUS);
|
|
||||||
gen_llvm_internal_op(ctx, TIMES);
|
|
||||||
gen_llvm_internal_op(ctx, DIVIDE);
|
|
||||||
|
|
||||||
for(auto& definition : prog) {
|
|
||||||
definition->gen_llvm_first(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(auto& definition : prog) {
|
|
||||||
definition->gen_llvm_second(ctx);
|
|
||||||
}
|
|
||||||
llvm::verifyModule(ctx.module);
|
|
||||||
ctx.module.print(llvm::outs(), nullptr);
|
|
||||||
output_llvm(ctx, "program.o");
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
yy::parser parser;
|
yy::parser parser;
|
||||||
type_mgr mgr;
|
type_mgr mgr;
|
||||||
@ -160,7 +74,6 @@ int main() {
|
|||||||
try {
|
try {
|
||||||
typecheck_program(program, mgr, env);
|
typecheck_program(program, mgr, env);
|
||||||
compile_program(program);
|
compile_program(program);
|
||||||
gen_llvm(program);
|
|
||||||
} catch(unification_error& err) {
|
} catch(unification_error& err) {
|
||||||
std::cout << "failed to unify types: " << std::endl;
|
std::cout << "failed to unify types: " << std::endl;
|
||||||
std::cout << " (1) \033[34m";
|
std::cout << " (1) \033[34m";
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "ast.hpp"
|
#include "ast.hpp"
|
||||||
#include "definition.hpp"
|
|
||||||
#include "parser.hpp"
|
#include "parser.hpp"
|
||||||
|
|
||||||
std::vector<definition_ptr> program;
|
std::vector<definition_ptr> program;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include "runtime.h"
|
#include "runtime.h"
|
||||||
|
|
||||||
struct node_base* alloc_node() {
|
struct node_base* alloc_node() {
|
||||||
@ -98,7 +97,7 @@ void stack_pack(struct stack* s, size_t n, int8_t t) {
|
|||||||
|
|
||||||
struct node_base** data = malloc(sizeof(*data) * n);
|
struct node_base** data = malloc(sizeof(*data) * n);
|
||||||
assert(data != NULL);
|
assert(data != NULL);
|
||||||
memcpy(data, &s->data[s->count - n], n * sizeof(*data));
|
memcpy(data, &s->data[s->count - 1 - n], n * sizeof(*data));
|
||||||
|
|
||||||
struct node_data* new_node = (struct node_data*) alloc_node();
|
struct node_data* new_node = (struct node_data*) alloc_node();
|
||||||
new_node->array = data;
|
new_node->array = data;
|
||||||
@ -154,30 +153,7 @@ struct node_base* eval(struct node_base* n) {
|
|||||||
|
|
||||||
extern void f_main(struct stack* s);
|
extern void f_main(struct stack* s);
|
||||||
|
|
||||||
void print_node(struct node_base* n) {
|
|
||||||
if(n->tag == NODE_APP) {
|
|
||||||
struct node_app* app = (struct node_app*) n;
|
|
||||||
print_node(app->left);
|
|
||||||
putchar(' ');
|
|
||||||
print_node(app->right);
|
|
||||||
} else if(n->tag == NODE_DATA) {
|
|
||||||
printf("(Packed)");
|
|
||||||
} else if(n->tag == NODE_GLOBAL) {
|
|
||||||
struct node_global* global = (struct node_global*) n;
|
|
||||||
printf("(Global: %p)", global->function);
|
|
||||||
} else if(n->tag == NODE_IND) {
|
|
||||||
print_node(((struct node_ind*) n)->next);
|
|
||||||
} else if(n->tag == NODE_NUM) {
|
|
||||||
struct node_num* num = (struct node_num*) n;
|
|
||||||
printf("%d", num->value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
struct node_global* first_node = alloc_global(f_main, 0);
|
struct node_global* first_node = alloc_global(f_main, 0);
|
||||||
struct node_base* result = eval((struct node_base*) first_node);
|
struct node_base* result = eval((struct node_base*) first_node);
|
||||||
|
|
||||||
printf("Result: ");
|
|
||||||
print_node(result);
|
|
||||||
putchar('\n');
|
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
%{
|
%{
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "ast.hpp"
|
#include "ast.hpp"
|
||||||
#include "definition.hpp"
|
|
||||||
#include "parser.hpp"
|
#include "parser.hpp"
|
||||||
|
|
||||||
#define YY_DECL yy::parser::symbol_type yylex()
|
#define YY_DECL yy::parser::symbol_type yylex()
|
||||||
|
@ -54,6 +54,7 @@ a `Module` object, which represents some collection of code and declarations
|
|||||||
|
|
||||||
{{< codeblock "C++" "compiler/08/llvm_context.hpp" >}}
|
{{< codeblock "C++" "compiler/08/llvm_context.hpp" >}}
|
||||||
|
|
||||||
|
{{< todo >}} Consistently name context / state.{{< /todo >}}
|
||||||
{{< todo >}} Explain creation functions. {{< /todo >}}
|
{{< todo >}} Explain creation functions. {{< /todo >}}
|
||||||
|
|
||||||
We include the LLVM context, builder, and module as members
|
We include the LLVM context, builder, and module as members
|
||||||
@ -81,58 +82,35 @@ an `llvm::LinkageType`, the name of the function, and the module
|
|||||||
in which the function is declared. Since we only have one
|
in which the function is declared. Since we only have one
|
||||||
module (the one we initialized in the constructor) that's
|
module (the one we initialized in the constructor) that's
|
||||||
the module we pass in. The name of the function is the same
|
the module we pass in. The name of the function is the same
|
||||||
as its name in the runtime. The linkage type is a little
|
as its name in the runtime, and the linkage type is always
|
||||||
more complicated - it tells LLVM the "visibility" of a function.
|
external. The only remaining parameter is
|
||||||
"Private" or "Internal" would hide this function from the linker
|
the `llvm::FunctionType`, which is created using code like:
|
||||||
(like `static` functions in C). However, we want to do the opposite: our
|
|
||||||
generated functions should be accessible from other code.
|
|
||||||
Thus, our linkage type is "External".
|
|
||||||
|
|
||||||
The only remaining parameter is the `llvm::FunctionType`, which
|
{{< todo >}} Why external? {{< /todo >}}
|
||||||
is created using code like:
|
|
||||||
|
|
||||||
```C++
|
```C++
|
||||||
llvm::FunctionType::get(return_type, {param_type_1, param_type_2, ...}, is_variadic)
|
llvm::FunctionType::get(return_type, {param_type_1, param_type_2, ...}, is_variadic)
|
||||||
```
|
```
|
||||||
|
|
||||||
Declaring all the functions and types in our runtime is mostly
|
Declaring all the functions and types in our runtime is mostly
|
||||||
just tedious. Here are a few lines from `create_functions()`, which
|
just tedious. Here are a few lines from `create_types()`, from
|
||||||
give a very good idea of the rest of that method:
|
|
||||||
|
|
||||||
{{< codelines "C++" "compiler/08/llvm_context.cpp" 47 60 >}}
|
|
||||||
|
|
||||||
Similarly, here are a few lines from `create_types()`, from
|
|
||||||
which you can extrapolate the rest:
|
which you can extrapolate the rest:
|
||||||
|
|
||||||
{{< codelines "C++" "compiler/08/llvm_context.cpp" 7 11 >}}
|
{{< codelines "C++" "compiler/08/llvm_context.cpp" 7 11 >}}
|
||||||
|
|
||||||
We also tell LLVM the contents of our structs, so that
|
{{< todo >}} Also show struct body setters. {{< /todo >}}
|
||||||
we may later reference specific fields. This is just like
|
|
||||||
forward declaration - we can forward declare a struct
|
|
||||||
in C/C++, but unless we also declare its contents,
|
|
||||||
we can't access what's inside. Below is the code
|
|
||||||
for specifying the body of `node_base` and `node_app`.
|
|
||||||
|
|
||||||
{{< codelines "C++" "compiler/08/llvm_context.cpp" 19 26 >}}
|
Similarly, here are a few lines from `create_functions()`, which
|
||||||
|
give a very good idea of the rest of that method:
|
||||||
|
|
||||||
There's still more functionality packed into `llvm_context`.
|
{{< codelines "C++" "compiler/08/llvm_context.cpp" 20 27 >}}
|
||||||
Let's next take a look into `custom_function`, and
|
|
||||||
the `create_custom_function` method. Why do we need
|
|
||||||
these?
|
|
||||||
|
|
||||||
This isn't the end of our `llvm_context` class: it also
|
This completes our implementation of the context.
|
||||||
has a variety of `create_*` methods! Let's take a look
|
|
||||||
at their signatures. Most return either `void`,
|
|
||||||
`llvm::ConstantInt*`, or `llvm::Value*`. Since
|
|
||||||
`llvm::ConstantInt*` is a subclass of `llvm::Value*`, let's
|
|
||||||
just treat it as simply an `llvm::Value*` while trying
|
|
||||||
to understand these methods.
|
|
||||||
|
|
||||||
So, what is `llvm::Value`? To answer this question, let's
|
|
||||||
first understand how the LLVM IR works.
|
|
||||||
|
|
||||||
### LLVM IR
|
### LLVM IR
|
||||||
An important property of LLVM IR is that it is in __Single Static Assignment__
|
It's now time to look at generating actual code for each G-machine instruction.
|
||||||
|
Before we do this, we need to get a little bit of an understanding of what LLVM
|
||||||
|
IR is like. An important property of LLVM IR is that it is in __Single Static Assignment__
|
||||||
(SSA) form. This means that each variable can only be assigned to once. For instance,
|
(SSA) form. This means that each variable can only be assigned to once. For instance,
|
||||||
if we use `<-` to represent assignment, the following program is valid:
|
if we use `<-` to represent assignment, the following program is valid:
|
||||||
|
|
||||||
@ -162,26 +140,13 @@ x2 <- x1 + 1
|
|||||||
In practice, LLVM's C++ API can take care of versioning variables on its own, by
|
In practice, LLVM's C++ API can take care of versioning variables on its own, by
|
||||||
auto-incrementing numbers associated with each variable we use.
|
auto-incrementing numbers associated with each variable we use.
|
||||||
|
|
||||||
Assigned to each variable is `llvm::Value`. The LLVM documentation states:
|
We need not get too deep into the specifics of LLVM IR's textual
|
||||||
|
representation, since we will largely be working with the C++
|
||||||
> It is the base class of all values computed by a program that may be used as operands to other values.
|
API to interact with it. We do, however, need to understand one more
|
||||||
|
concept from the world of compiler design: __basic blocks__. A basic
|
||||||
It's important to understand that `llvm::Value` __does not store the result of the computation__.
|
block is a sequence of instructions that are guaranteed to be executed
|
||||||
It rather represents how something may be computed. 1 is a value because it computed by
|
one after another. This means that a basic block cannot have
|
||||||
just returning. `x + 1` is a value because it is computed by adding the value inside of
|
an if/else, jump, or any other type of control flow anywhere
|
||||||
`x` to 1. Since we cannot modify a variable once we've declared it, we will
|
|
||||||
keep assigning intermediate results to new variables, constructing new values
|
|
||||||
out of values that we've already specified.
|
|
||||||
|
|
||||||
This somewhat elucidates what the `create_*` functions do: `create_i8` creates an 8-bit integer
|
|
||||||
value, and `create_pop` creates a value that is computed by calling
|
|
||||||
our runtime `stack_pop` function.
|
|
||||||
|
|
||||||
Before we move on to look at the implementations of these functions,
|
|
||||||
we need to understand another concept from the world of compiler design:
|
|
||||||
__basic blocks__. A basic block is a sequence of instructions that
|
|
||||||
are guaranteed to be executed one after another. This means that a
|
|
||||||
basic block cannot have an if/else, jump, or any other type of control flow anywhere
|
|
||||||
except at the end. If control flow could appear inside the basic block,
|
except at the end. If control flow could appear inside the basic block,
|
||||||
there would be opporunity for execution of some, but not all,
|
there would be opporunity for execution of some, but not all,
|
||||||
instructions in the block, violating the definition. Every time
|
instructions in the block, violating the definition. Every time
|
||||||
@ -190,74 +155,7 @@ Writing control flow involves creating several blocks, with each
|
|||||||
block serving as the destination of a potential jump. We will
|
block serving as the destination of a potential jump. We will
|
||||||
see this used to compile the Jump instruction.
|
see this used to compile the Jump instruction.
|
||||||
|
|
||||||
### Generating LLVM IR
|
### Generating LLVM
|
||||||
Now that we understand what `llvm::Value` is, and have a vague
|
|
||||||
understanding of how LLVM is structured, let's take a look at
|
|
||||||
the implementations of the `create_*` functions. The simplest
|
|
||||||
is `create_i8`:
|
|
||||||
|
|
||||||
{{< codelines "C++" "compiler/08/llvm_context.cpp" 150 152 >}}
|
|
||||||
|
|
||||||
Not much to see here. We create an instance of the `llvm::ConstantInt` class,
|
|
||||||
from the actual integer given to the method. As we said before,
|
|
||||||
`llvm::ConstantInt` is a subclass of `llvm::Value`. Next up, let's look
|
|
||||||
at `create_pop`:
|
|
||||||
|
|
||||||
{{< codelines "C++" "compiler/08/llvm_context.cpp" 160 163 >}}
|
|
||||||
|
|
||||||
We first retrieve an `llvm::Function` associated with `stack_pop`
|
|
||||||
from our map, and then use `llvm::IRBuilder::CreateCall` to insert
|
|
||||||
a value that represents a function call into the currently
|
|
||||||
selected basic block (the builder's state is what
|
|
||||||
dictates what the "selected basic block" is). `CreateCall`
|
|
||||||
takes as parameters the function we want to call (`stack_pop`,
|
|
||||||
which we store into the `pop_f` variable), as well as the arguments
|
|
||||||
to the function (for which we pass `f->arg_begin()`).
|
|
||||||
|
|
||||||
Hold on. What the heck is `arg_begin()`? Why do we take a function
|
|
||||||
as a paramter to this method? The answer is fairly simple: this
|
|
||||||
method is used when we are
|
|
||||||
generating a function with signature `void f_(struct stack* s)`
|
|
||||||
(we discussed the signature in the previous post). The
|
|
||||||
parameter that we give to `create_pop` is this function we're
|
|
||||||
generating, and `arg_begin()` gets the value that represents
|
|
||||||
the first parameter to our function - `s`! Since `stack_pop`
|
|
||||||
takes a stack, we need to give it the stack we're working on,
|
|
||||||
and so we use `f->arg_begin()` to access it.
|
|
||||||
|
|
||||||
Most of the other functions follow this exact pattern, with small
|
|
||||||
deviations. However, another function uses a more complicated LLVM
|
|
||||||
instruction:
|
|
||||||
|
|
||||||
{{< codelines "C++" "compiler/08/llvm_context.cpp" 202 209 >}}
|
|
||||||
|
|
||||||
`unwrap_num` is used to cast a given node pointer to a pointer
|
|
||||||
to a number node, and then return the integer value from
|
|
||||||
that number node. It starts fairly innocently: we ask
|
|
||||||
LLVM for the type of a pointer to a `node_num` struct,
|
|
||||||
and then use `CreatePointerCast` to create a value
|
|
||||||
that is the same node pointer we're given, but now interpreted
|
|
||||||
as a number node pointer. We now have to access
|
|
||||||
the `value` field of our node. `CreateGEP` helps us with
|
|
||||||
this: given a pointer to a node, and two offsets
|
|
||||||
`n` and `k`, it effectively performs the following:
|
|
||||||
|
|
||||||
```C++
|
|
||||||
&(num_pointer[n]->kth_field)
|
|
||||||
```
|
|
||||||
|
|
||||||
The first offset, then, gives an index into the "array"
|
|
||||||
represented by the pointer, while the second offset
|
|
||||||
gives the index of the field we want to access. We
|
|
||||||
want to dereference the pointer (`num_pointer[0]`),
|
|
||||||
and we want the second field (`1`, when counting from 0).
|
|
||||||
Thus, we call CreateGEP with these offsets and our pointers.
|
|
||||||
|
|
||||||
This still leaves us with a pointer to a number, rather
|
|
||||||
than the number itself. To dereference the pointer, we use
|
|
||||||
`CreateLoad`. This gives us the value of the number node,
|
|
||||||
which we promptly return.
|
|
||||||
|
|
||||||
Let's envision a `gen_llvm` method on the `instruction` struct.
|
Let's envision a `gen_llvm` method on the `instruction` struct.
|
||||||
We need access to all the other functions from our runtime,
|
We need access to all the other functions from our runtime,
|
||||||
such as `stack_init`, and functions from our program such
|
such as `stack_init`, and functions from our program such
|
||||||
|
Loading…
Reference in New Issue
Block a user