diff --git a/Makefile b/Makefile index 85bc0b3..f3718b5 100644 --- a/Makefile +++ b/Makefile @@ -78,6 +78,7 @@ ELC_SRCS := \ c.c \ cl.c \ cpp.c \ + cpp_template.c \ cr.c \ cs.c \ el.c \ @@ -311,6 +312,14 @@ TOOL := g++-6 include target.mk $(OUT.eir.cpp.out): tools/runcpp.sh +ifdef CPP_TEMPLATE +TARGET := cpp_template +RUNNER := tools/runcpp_template.sh +TOOL := g++ +include target.mk +$(OUT.eir.cpp_template.out): tools/runcpp_template.sh +endif + ifeq ($(uname),Linux) TARGET := $(ARCH) RUNNER := diff --git a/README.md b/README.md index 3c48c24..d4ff6b1 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,8 @@ Currently, there are 31 backends: * Befunge * Brainfuck * C -* C++14 (compile-time) (by [@kw-udon](https://github.com/kw-udon/)) +* C++14 constexpr (compile-time) (by [@kw-udon](https://github.com/kw-udon/)) +* C++ Template Metaprogramming (compile-time) (by [@kw-udon](https://github.com/kw-udon/)) (WIP) * C# (by [@masaedw](https://github.com/masaedw/)) * C-INTERCAL * CommonLisp (by [@youz](https://github.com/youz/)) @@ -214,7 +215,7 @@ You can find more descriptions and released vim script in This backend was contributed by [@hak7a3](https://github.com/hak7a3/). See also [8cc.tex](https://github.com/hak7a3/8cc.tex). -### C++14 (compile-time) +### C++14 constexpr (compile-time) This backend was contributed by [@kw-udon](https://github.com/kw-udon/). You can find more descriptions in diff --git a/target/cpp_template.c b/target/cpp_template.c new file mode 100644 index 0000000..4cf9324 --- /dev/null +++ b/target/cpp_template.c @@ -0,0 +1,243 @@ +#include +#include +#include + +static void cpp_template_emit_file_prologue(void) { + emit_line("#include "); + emit_line(""); + emit_line(cpp_template_lib); + emit_line(""); + emit_line("typedef enum { a, b, c, d, bp, sp } Reg_Name;"); +} + +static const char* cpp_template_op_str(Inst* inst) { + const char * op_str; + const char * src_str = inst->src.type == REG ? "reg" : "imm"; + switch (inst->op) { + case MOV: + op_str = "mov"; break; + case ADD: + op_str = "add"; break; + case SUB: + op_str = "sub"; break; + case LOAD: + op_str = "load"; break; + case STORE: + op_str = "store"; break; + case PUTC: + op_str = "putc"; break; + case EQ: + case NE: + case LT: + case GT: + case LE: + case GE: + op_str = "cmp"; break; + case JEQ: + case JNE: + case JLT: + case JGT: + case JLE: + case JGE: + op_str = "jcmp"; break; + default: + error("oops"); + } + return format("%s_%s", op_str, src_str); +} + + +int reg_id = 0; +int mem_id = 0; +int buf_id = 0; +int exit_flag = 0; + +static void cpp_template_emit_inst(Inst* inst) { + switch (inst->op) { + case MOV: + case ADD: + case SUB: + emit_line("typedef %s r%d;", + cpp_template_op_str(inst), + reg_id, reg_names[inst->dst.reg], src_str(inst), reg_id + 1); + reg_id++; + break; + + case LOAD: + emit_line("typedef %s r%d;", + cpp_template_op_str(inst), + reg_id, mem_id, reg_names[inst->dst.reg], src_str(inst), reg_id + 1); + reg_id++; + break; + + case STORE: + emit_line("typedef %s m%d;", + cpp_template_op_str(inst), + reg_id, mem_id, reg_names[inst->dst.reg], src_str(inst), mem_id + 1); + mem_id++; + break; + + case PUTC: + emit_line("typedef %s b%d;", + cpp_template_op_str(inst), + reg_id, buf_id, src_str(inst), buf_id + 1); + buf_id++; + break; + + case GETC: + emit_line("typedef getc_reg r%d;", + reg_id, reg_names[inst->dst.reg], reg_id + 1); + reg_id++; + break; + + case EXIT: + emit_line("typedef exit_inst r%d;", + reg_id, reg_id + 1); + exit_flag = 1; + reg_id++; + return; + + case DUMP: + break; + + case EQ: + case NE: + case LT: + case GT: + case LE: + case GE: + emit_line("typedef %s r%d;", + cpp_template_op_str(inst), + reg_id, + reg_names[inst->dst.reg], + src_str(inst), + inst->op - EQ, + reg_id + 1); + reg_id++; + break; + + case JEQ: + case JNE: + case JLT: + case JGT: + case JLE: + case JGE: + emit_line("typedef %s r%d;", + cpp_template_op_str(inst), + reg_id, + value_str(&inst->jmp), + reg_names[inst->dst.reg], + src_str(inst), + inst->op - JEQ, + reg_id + 1); + reg_id++; + break; + + case JMP: + emit_line("typedef jmp_%s r%d;", + inst->jmp.type == REG ? "reg" : "imm", + reg_id, value_str(&inst->jmp), reg_id + 1); + reg_id++; + break; + + default: + error("oops"); + } + return; +} + +static void cpp_template_emit_pc_change(int pc) { + emit_line("typedef inc_pc result;", reg_id, mem_id, buf_id); + dec_indent(); + emit_line("};"); + emit_line(""); + emit_line("template "); + emit_line("struct func_switch_impl {", pc); + inc_indent(); +} + +static void cpp_template_emit_func_epilogue() { + emit_line("typedef inc_pc result;", reg_id, mem_id, buf_id); + dec_indent(); + emit_line("};"); + emit_line(""); + emit_line("template "); + emit_line("struct func_switch : func_switch_impl<" + "typename env::regs, typename env::mem, " + "typename env::buf, pc>::result {};"); +} + +void cpp_template_emit_func_switch_impl(Inst* inst) { + int prev_pc = 0; + emit_line("template "); + emit_line("struct func_switch_impl { typedef inc_pc result; };"); + emit_line(""); + emit_line("template "); + emit_line("struct func_switch_impl {"); + inc_indent(); + for (; inst; inst = inst->next) { + if (prev_pc != inst->pc) { + cpp_template_emit_pc_change(inst->pc); + reg_id = 0; + mem_id = 0; + buf_id = 0; + exit_flag = 0; + } + prev_pc = inst->pc; + + cpp_template_emit_inst(inst); + } + cpp_template_emit_func_epilogue(); +} + +static void cpp_template_emit_main_loop(void) { + emit_line("template "); + emit_line("struct main_loop { typedef env result; };"); + emit_line("template "); + emit_line("struct main_loop {"); + inc_indent(); + emit_line("typedef func_switch env2;"); + emit_line("typedef typename main_loop::result result;"); + dec_indent(); + emit_line("};"); +} + +static void cpp_template_emit_calc_main(Data* data) { + emit_line("struct calc_main {"); + inc_indent(); + emit_line("typedef init_memory mem0;"); + int mp; + for (mp = 0; data; data = data->next, mp++) { + if (data->v) { + emit_line("typedef store_value mem%d;", mp, mp, data->v, mp + 1); + } else { + emit_line("typedef mem%d mem%d;", mp, mp + 1); + } + } + emit_line("typedef init_regs<0> regs;"); + emit_line(" typedef make_env env;", mp); + emit_line("typedef main_loop::result result;"); + dec_indent(); + emit_line("};"); +} + +void target_cpp_template(Module* module) { + cpp_template_emit_file_prologue(); + emit_line(""); + + cpp_template_emit_func_switch_impl(module->text); + emit_line(""); + + cpp_template_emit_main_loop(); + emit_line(""); + + cpp_template_emit_calc_main(module->data); + emit_line(""); + + emit_line("int main() {"); + inc_indent(); + emit_line("typedef calc_main::result result;"); + emit_line("print_buffer::run();"); + dec_indent(); + emit_line("}"); +} diff --git a/target/cpp_template_lib.h b/target/cpp_template_lib.h new file mode 100644 index 0000000..d9fa81f --- /dev/null +++ b/target/cpp_template_lib.h @@ -0,0 +1,326 @@ +/* + Library for C++ Template Metaprogramming backend + + Original Version: + https://gist.github.com/kw-udon/6248197ec8fdc681b63e4cb8271fac56 +*/ + +static char cpp_template_lib[] = + "// Library for ELVM's C++ Template Metaprogramming backend\n" + "\n" + "// Input Buffer\n" + "constexpr static const char* input =\n" + "# include \"input.txt\"\n" + ";\n" + "\n" + "// Memory Size\n" + "const int MEM_SIZE = 1 << 16;\n" + "\n" + "// Data Structures\n" + "\n" + "template struct Int {static const int val = n;};\n" + "template struct Bool{static const bool val = b;};\n" + "\n" + "// List\n" + "struct Nil { typedef Nil head; typedef Nil tail; };\n" + "template \n" + "struct Pair { typedef Head head; typedef Tail tail; };\n" + "\n" + "template\n" + "struct is_nil : Bool {};\n" + "template<>\n" + "struct is_nil : Bool {};\n" + "\n" + "template \n" + "struct cons : Pair {};\n" + "\n" + "template \n" + "struct get_at : get_at {};\n" + "template \n" + "struct get_at : list::head {};\n" + "\n" + "template \n" + "struct set_at : cons> {};\n" + "template \n" + "struct set_at : cons, typename list::tail> {};\n" + "\n" + "template \n" + "struct init_list_aux {\n" + " typedef typename init_list_aux::result tail;\n" + " typedef cons, tail> result;\n" + "};\n" + "template <>\n" + "struct init_list_aux<0> {\n" + " typedef Nil result;\n" + "};\n" + "template \n" + "struct init_list : init_list_aux::result {};\n" + "\n" + "// Binary Tree (for Memory)\n" + "template \n" + "struct Node {\n" + " typedef node_v value;\n" + " typedef lnode left;\n" + " typedef rnode right;\n" + " static const bool is_leaf = is_nil::val && is_nil::val;\n" + "};\n" + "template \n" + "struct Leaf : Node {};\n" + "\n" + "template \n" + "struct mk_tree {\n" + " typedef Int value;\n" + " typedef mk_tree left;\n" + " typedef mk_tree right;\n" + "};\n" + "template <>\n" + "struct mk_tree<0> : Leaf> {};\n" + "\n" + "template \n" + "struct get_child : tree::left {};\n" + "template \n" + "struct get_child : tree::right {};\n" + "template \n" + "struct update_child : tree { typedef child left; };\n" + "template \n" + "struct update_child : tree {typedef child right;};\n" + "\n" + "\n" + "// Memory\n" + "\n" + "constexpr int const_log2(int x) {\n" + " return x < 2 ? x : 1 + const_log2(x >> 1);\n" + "}\n" + "template \n" + "struct init_memory {\n" + " static const int depth = const_log2(mem_size); // memory size == 2^d\n" + " typedef mk_tree tree;\n" + "};\n" + "\n" + "// Memory Access\n" + "// Load\n" + "template \n" + "struct load_value_aux {\n" + " static const int flg = (idx >> (depth-1)) & 1;\n" + " typedef get_child child;\n" + " typedef typename load_value_aux::result result;\n" + "};\n" + "\n" + "template \n" + "struct load_value_aux {\n" + " typedef typename tree::value result;\n" + "};\n" + "\n" + "template \n" + "struct load_value : load_value_aux::result {};\n" + "\n" + "// Store\n" + "template \n" + "struct store_value_aux {\n" + " static const int flg = (idx >> (depth-1)) & 1;\n" + " typedef get_child child;\n" + " typedef typename store_value_aux::result new_child;\n" + " typedef update_child result;\n" + "};\n" + "template \n" + "struct store_value_aux {\n" + " typedef Leaf> result;\n" + "};\n" + "\n" + "template \n" + "struct store_value : memory {\n" + " typedef typename store_value_aux::result tree;\n" + "};\n" + "\n" + "// Registers\n" + "template \n" + "struct init_regs {\n" + " static const int a = 0;\n" + " static const int b = 0;\n" + " static const int c = 0;\n" + " static const int d = 0;\n" + " static const int sp = 0;\n" + " static const int bp = 0;\n" + " static const int pc = PC;\n" + " static const bool exit_flag = false;\n" + " static const int input_cur = 0;\n" + "};\n" + "\n" + "template \n" + "struct reg_val {};\n" + "template \n" + "struct reg_val { static const int val = regs::a;};\n" + "template \n" + "struct reg_val { static const int val = regs::b;};\n" + "template \n" + "struct reg_val { static const int val = regs::c;};\n" + "template \n" + "struct reg_val { static const int val = regs::d;};\n" + "template \n" + "struct reg_val { static const int val = regs::sp;};\n" + "template \n" + "struct reg_val { static const int val = regs::bp;};\n" + "\n" + "\n" + "// Environment (Tuple of Registers, Memory and Buffer)\n" + "template \n" + "struct make_env {\n" + " typedef Regs regs;\n" + " typedef Mem mem;\n" + " typedef Buf buf;\n" + "};\n" + "\n" + "template \n" + "struct update_pc : regs {static const int pc = PC;};\n" + "template \n" + "struct inc_input_cur : regs {static const int input_cur = regs::input_cur + (input[regs::input_cur] ? 1 : 0);};\n" + "template \n" + "struct inc_pc : make_env, mem, buf> {};\n" + "\n" + "\n" + "// Functions for Each Instruction\n" + "\n" + "// MOV\n" + "template \n" + "struct mov_imm_aux : r {};\n" + "template \n" + "struct mov_imm_aux : r {static const int a = imm;};\n" + "template \n" + "struct mov_imm_aux : r {static const int b = imm;};\n" + "template \n" + "struct mov_imm_aux : r {static const int c = imm;};\n" + "template \n" + "struct mov_imm_aux : r {static const int d = imm;};\n" + "template \n" + "struct mov_imm_aux : r {static const int sp = imm;};\n" + "template \n" + "struct mov_imm_aux : r {static const int bp = imm;};\n" + "\n" + "template \n" + "struct mov_imm : mov_imm_aux {};\n" + "template \n" + "struct mov_reg : mov_imm_aux::val> {};\n" + "\n" + "// ADD, SUB\n" + "template \n" + "struct add_aux {\n" + " static const int dst_val = reg_val::val;\n" + " typedef mov_imm result;\n" + "};\n" + "template \n" + "struct add_imm : add_aux::result {};\n" + "template \n" + "struct add_reg : add_aux::val>::result {};\n" + "template \n" + "struct sub_imm : add_aux::result {};\n" + "template \n" + "struct sub_reg : add_aux::val>::result {};\n" + "\n" + "// LOAD\n" + "template \n" + "struct load_aux : load_value {};\n" + "\n" + "template \n" + "struct load_imm : mov_imm::val> {};\n" + "template \n" + "struct load_reg : load_imm::val> {};\n" + "\n" + "// STORE\n" + "template \n" + "struct store_aux {\n" + " typedef reg_val data;\n" + " typedef store_value result;\n" + "};\n" + "\n" + "template \n" + "struct store_imm : store_aux::result {};\n" + "template \n" + "struct store_reg : store_imm::val> {};\n" + "\n" + "// PUTC\n" + "template \n" + "struct putc_aux {\n" + " typedef cons, buffer> result;\n" + "};\n" + "template \n" + "struct putc_imm : putc_aux::result {};\n" + "template \n" + "struct putc_reg : putc_imm::val> {};\n" + "\n" + "// GETC\n" + "template \n" + "struct getc_aux {\n" + " static const int read_val = input[regs::input_cur];\n" + " typedef inc_input_cur regs2;\n" + " typedef mov_imm result;\n" + "};\n" + "template \n" + "struct getc_reg : getc_aux::result {};\n" + "\n" + "\n" + "// EXIT\n" + "template \n" + "struct exit_inst : regs {\n" + " static const int pc = 1000000;\n" + " static const bool exit_flag = true;\n" + "};\n" + "\n" + "// JMP\n" + "template \n" + "struct jmp_aux : update_pc{};\n" + "template \n" + "struct jmp_aux : regs {};\n" + "\n" + "template \n" + "struct jmp_imm : jmp_aux {};\n" + "template \n" + "struct jmp_reg : jmp_aux::val - 1, 1> {};\n" + "\n" + "// CMP\n" + "template \n" + "struct eq_imm {\n" + " static const int reg_v = reg_val::val;\n" + " static const bool result = (reg_v == imm);\n" + "};\n" + "\n" + "template \n" + "struct cmp_op_imm {\n" + " static const int v = reg_val::val;\n" + " static const int result =\n" + " op == 0 ? (v == imm) :\n" + " op == 1 ? (v != imm) :\n" + " op == 2 ? (v < imm) :\n" + " op == 3 ? (v > imm) :\n" + " op == 4 ? (v <= imm) :\n" + " op == 5 ? (v >= imm) :\n" + " 1;\n" + "};\n" + "\n" + "template \n" + "struct cmp_op_reg : cmp_op_imm::val, op> {};\n" + "\n" + "template \n" + "struct cmp_imm : mov_imm::result> {};\n" + "template \n" + "struct cmp_reg : mov_imm::result> {};\n" + "\n" + "// JCMP\n" + "template \n" + "struct jcmp_imm : jmp_aux::result> {};\n" + "template \n" + "struct jcmp_reg : jmp_aux::result> {};\n" + "\n" + "// print_buffer\n" + "template \n" + "struct print_buffer_aux {\n" + " static inline void run () {\n" + " print_buffer_aux::run();\n" + " putchar(list::head::val);\n" + " }\n" + "};\n" + "template <>\n" + "struct print_buffer_aux { static inline void run () {}};\n" + "template \n" + "struct print_buffer : print_buffer_aux {};\n" + "\n" + "// Library ends here\n"; diff --git a/target/elc.c b/target/elc.c index e3713f8..d0f3f9f 100644 --- a/target/elc.c +++ b/target/elc.c @@ -11,6 +11,7 @@ void target_bf(Module* module); void target_c(Module* module); void target_cl(Module* module); void target_cpp(Module* module); +void target_cpp_template(Module* module); void target_cr(Module* module); void target_cs(Module* module); void target_el(Module* module); @@ -51,6 +52,7 @@ static target_func_t get_target_func(const char* ext) { if (!strcmp(ext, "c")) return target_c; if (!strcmp(ext, "cl")) return target_cl; if (!strcmp(ext, "cpp")) return target_cpp; + if (!strcmp(ext, "cpp_template")) return target_cpp_template; if (!strcmp(ext, "cr")) return target_cr; if (!strcmp(ext, "cs")) return target_cs; if (!strcmp(ext, "el")) return target_el; diff --git a/tools/runcpp_template.sh b/tools/runcpp_template.sh new file mode 100755 index 0000000..5a5fdb8 --- /dev/null +++ b/tools/runcpp_template.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +set -e + +dir=$(mktemp -d) +mkdir -p $dir +cp $1 $dir/$(basename $1).cpp +infile=${dir}/input.txt + +cat > $infile + +if [ ! -s $infile ]; then + echo '""' > $infile +else + sed -i '1s/^/R"(/' $infile + echo ')"' >> $infile +fi + +g++ -std=c++11 ${dir}/$(basename $1).cpp -o $1.exe -ftemplate-depth=2147483647 +rm -fr $dir +./$1.exe