Compare commits
4 Commits
c79b5a4120
...
1a8a1c3052
Author | SHA1 | Date | |
---|---|---|---|
1a8a1c3052 | |||
2994f8983d | |||
64227f2873 | |||
9aef499deb |
|
@ -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_name(op))));
|
into.push_back(instruction_ptr(new instruction_pushglobal(op_action(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,6 +46,7 @@ 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,6 +48,7 @@ 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) {
|
||||||
|
@ -56,6 +57,7 @@ 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,6 +19,11 @@ 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,6 +41,15 @@ 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_name(op))));
|
into.push_back(instruction_ptr(new instruction_pushglobal(op_action(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,6 +46,7 @@ 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,6 +48,7 @@ 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) {
|
||||||
|
@ -56,6 +57,7 @@ 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,6 +19,11 @@ 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,6 +41,15 @@ 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,5 +1,6 @@
|
||||||
#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) {
|
||||||
|
@ -104,7 +105,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_name(op))));
|
into.push_back(instruction_ptr(new instruction_pushglobal(op_action(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,27 +43,6 @@ 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;
|
||||||
|
|
||||||
|
@ -160,37 +139,3 @@ 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,5 +1,10 @@
|
||||||
#include "ast.hpp"
|
#include "definition.hpp"
|
||||||
#include "error.hpp"
|
#include "error.hpp"
|
||||||
|
#include "ast.hpp"
|
||||||
|
#include "llvm_context.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();
|
||||||
|
@ -48,6 +53,18 @@ 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) {
|
||||||
|
@ -56,6 +73,7 @@ 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;
|
||||||
|
@ -79,3 +97,20 @@ 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
|
||||||
|
}
|
||||||
|
|
73
code/compiler/08/definition.hpp
Normal file
73
code/compiler/08/definition.hpp
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
#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,5 +1,6 @@
|
||||||
#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;
|
||||||
|
@ -23,7 +24,9 @@ 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 {
|
||||||
// TODO
|
auto& global_f = ctx.custom_functions.at("f_" + name);
|
||||||
|
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 {
|
||||||
|
@ -35,6 +38,15 @@ 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;
|
||||||
|
@ -87,7 +99,27 @@ 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 {
|
||||||
// TODO
|
auto top_node = ctx.create_peek(f, ctx.create_size(0));
|
||||||
|
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,6 +47,16 @@ 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_push",
|
"stack_pop",
|
||||||
&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_push",
|
"stack_peek",
|
||||||
&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_push",
|
"stack_popn",
|
||||||
&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_push",
|
"stack_slide",
|
||||||
&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_push",
|
"stack_update",
|
||||||
&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_push",
|
"stack_alloc",
|
||||||
&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_push",
|
"stack_pack",
|
||||||
&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_push",
|
"stack_split",
|
||||||
&module
|
&module
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -147,13 +147,13 @@ void llvm_context::create_functions() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value* llvm_context::create_i8(int8_t i) {
|
ConstantInt* llvm_context::create_i8(int8_t i) {
|
||||||
return ConstantInt::get(ctx, APInt(8, i));
|
return ConstantInt::get(ctx, APInt(8, i));
|
||||||
}
|
}
|
||||||
Value* llvm_context::create_i32(int32_t i) {
|
ConstantInt* llvm_context::create_i32(int32_t i) {
|
||||||
return ConstantInt::get(ctx, APInt(32, i));
|
return ConstantInt::get(ctx, APInt(32, i));
|
||||||
}
|
}
|
||||||
Value* llvm_context::create_size(size_t i) {
|
ConstantInt* 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_size(0);
|
auto offset_0 = create_i32(0);
|
||||||
auto offset_1 = create_size(1);
|
auto offset_1 = create_i32(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,6 +212,15 @@ 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 });
|
||||||
|
@ -221,3 +230,23 @@ 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,10 +7,18 @@
|
||||||
#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;
|
||||||
|
|
||||||
|
@ -29,9 +37,9 @@ struct llvm_context {
|
||||||
void create_types();
|
void create_types();
|
||||||
void create_functions();
|
void create_functions();
|
||||||
|
|
||||||
llvm::Value* create_i8(int8_t);
|
llvm::ConstantInt* create_i8(int8_t);
|
||||||
llvm::Value* create_i32(int32_t);
|
llvm::ConstantInt* create_i32(int32_t);
|
||||||
llvm::Value* create_size(size_t);
|
llvm::ConstantInt* 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*);
|
||||||
|
@ -48,7 +56,11 @@ 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,8 +1,20 @@
|
||||||
#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;
|
||||||
|
@ -55,6 +67,80 @@ 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;
|
||||||
|
@ -74,6 +160,7 @@ 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,6 +2,7 @@
|
||||||
#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,6 +1,7 @@
|
||||||
#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() {
|
||||||
|
@ -97,7 +98,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 - 1 - n], n * sizeof(*data));
|
memcpy(data, &s->data[s->count - 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;
|
||||||
|
@ -153,7 +154,30 @@ 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,6 +3,7 @@
|
||||||
%{
|
%{
|
||||||
#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,7 +54,6 @@ 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
|
||||||
|
@ -82,35 +81,58 @@ 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, and the linkage type is always
|
as its name in the runtime. The linkage type is a little
|
||||||
external. The only remaining parameter is
|
more complicated - it tells LLVM the "visibility" of a function.
|
||||||
the `llvm::FunctionType`, which is created using code like:
|
"Private" or "Internal" would hide this function from the linker
|
||||||
|
(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".
|
||||||
|
|
||||||
{{< todo >}} Why external? {{< /todo >}}
|
The only remaining parameter is the `llvm::FunctionType`, which
|
||||||
|
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_types()`, from
|
just tedious. Here are a few lines from `create_functions()`, which
|
||||||
|
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 >}}
|
||||||
|
|
||||||
{{< todo >}} Also show struct body setters. {{< /todo >}}
|
We also tell LLVM the contents of our structs, so that
|
||||||
|
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`.
|
||||||
|
|
||||||
Similarly, here are a few lines from `create_functions()`, which
|
{{< codelines "C++" "compiler/08/llvm_context.cpp" 19 26 >}}
|
||||||
give a very good idea of the rest of that method:
|
|
||||||
|
|
||||||
{{< codelines "C++" "compiler/08/llvm_context.cpp" 20 27 >}}
|
There's still more functionality packed into `llvm_context`.
|
||||||
|
Let's next take a look into `custom_function`, and
|
||||||
|
the `create_custom_function` method. Why do we need
|
||||||
|
these?
|
||||||
|
|
||||||
This completes our implementation of the context.
|
This isn't the end of our `llvm_context` class: it also
|
||||||
|
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
|
||||||
It's now time to look at generating actual code for each G-machine instruction.
|
An important property of LLVM IR is that it is in __Single Static Assignment__
|
||||||
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:
|
||||||
|
|
||||||
|
@ -140,13 +162,26 @@ 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.
|
||||||
|
|
||||||
We need not get too deep into the specifics of LLVM IR's textual
|
Assigned to each variable is `llvm::Value`. The LLVM documentation states:
|
||||||
representation, since we will largely be working with the C++
|
|
||||||
API to interact with it. We do, however, need to understand one more
|
> It is the base class of all values computed by a program that may be used as operands to other values.
|
||||||
concept from the world of compiler design: __basic blocks__. A basic
|
|
||||||
block is a sequence of instructions that are guaranteed to be executed
|
It's important to understand that `llvm::Value` __does not store the result of the computation__.
|
||||||
one after another. This means that a basic block cannot have
|
It rather represents how something may be computed. 1 is a value because it computed by
|
||||||
an if/else, jump, or any other type of control flow anywhere
|
just returning. `x + 1` is a value because it is computed by adding the value inside of
|
||||||
|
`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
|
||||||
|
@ -155,7 +190,74 @@ 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
|
### Generating LLVM IR
|
||||||
|
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