From 7c1e1751d9ebfc6da48ccb3b7737096b0c28eb6f Mon Sep 17 00:00:00 2001 From: yhirose Date: Mon, 7 Aug 2017 23:43:16 -0400 Subject: [PATCH] Added LLVM support to PL/0 sample --- CMakeLists.txt | 1 - language/pl0/CMakeLists.txt | 10 - language/pl0/pl0.cc | 396 +++++++++++++++++++++++++++++++- language/pl0/pl0.sln | 34 --- language/pl0/pl0.vcxproj | 92 -------- language/pl0/samples/fib.pas | 2 +- language/pl0/samples/square.pas | 2 +- 7 files changed, 387 insertions(+), 150 deletions(-) delete mode 100644 language/pl0/CMakeLists.txt delete mode 100644 language/pl0/pl0.sln delete mode 100644 language/pl0/pl0.vcxproj diff --git a/CMakeLists.txt b/CMakeLists.txt index 96b902d..bb5f973 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,5 +69,4 @@ add_subdirectory(example) if(NOT MSVC) add_subdirectory(lint) - add_subdirectory(language/pl0) endif() diff --git a/language/pl0/CMakeLists.txt b/language/pl0/CMakeLists.txt deleted file mode 100644 index 91597ea..0000000 --- a/language/pl0/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -cmake_minimum_required(VERSION 2.8) -include_directories(../..) - -if(MSVC) - add_compile_options(${cxx11_options} /W0) -else() - add_compile_options(${cxx11_options}) -endif() - -add_executable(pl0 pl0.cc) diff --git a/language/pl0/pl0.cc b/language/pl0/pl0.cc index 1618e1c..79729a3 100644 --- a/language/pl0/pl0.cc +++ b/language/pl0/pl0.cc @@ -9,9 +9,16 @@ #include #include #include +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/ExecutionEngine/GenericValue.h" +#include "llvm/ExecutionEngine/MCJIT.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/ValueSymbolTable.h" +#include "llvm/Support/TargetSelect.h" using namespace peg; using namespace peg::udl; +using namespace llvm; using namespace std; /* @@ -154,7 +161,8 @@ struct SymbolTable { static void constants(const shared_ptr ast, shared_ptr scope) { - // const <- ('CONST' __ ident '=' _ number(',' _ ident '=' _ number)* ';' _) ? + // const <- ('CONST' __ ident '=' _ number(',' _ ident '=' _ number)* ';' + // _)? const auto& nodes = ast->nodes; for (auto i = 0u; i < nodes.size(); i += 2) { const auto& ident = nodes[i + 0]->token; @@ -224,7 +232,7 @@ struct SymbolTable { }; /* - * Environment + * Interpreter */ struct Environment { Environment(shared_ptr scope, shared_ptr outer) @@ -263,9 +271,6 @@ struct Environment { map variables; }; -/* - * Interpreter - */ struct Interpreter { static void exec(const shared_ptr ast, shared_ptr env = nullptr) { @@ -312,8 +317,7 @@ struct Interpreter { static void exec_statement(const shared_ptr ast, shared_ptr env) { - // statement <- (assignment / call / statements / if / while / out / - // in)? + // statement <- (assignment / call / statements / if / while / out / in)? if (!ast->nodes.empty()) { exec(ast->nodes[0], env); } @@ -487,6 +491,359 @@ struct Interpreter { } }; +/* + * LLVM + */ +struct LLVM { + LLVM() : builder_(context_) { + module_ = make_unique("pl0", context_); + } + + void compile(const shared_ptr ast) { + InitializeNativeTarget(); + InitializeNativeTargetAsmPrinter(); + compile_libs(); + compile_program(ast); + } + + void dump() { module_->dump(); } + + static void exec(const shared_ptr ast) { + LLVM compiler; + compiler.compile(ast); + // compiler.dump(); + + auto EE = EngineBuilder(std::move(compiler.module_)).create(); + std::unique_ptr ExecutionEngineOwner(EE); + + std::vector noargs; + auto fn = EE->FindFunctionNamed("__main__"); + auto ret = EE->runFunction(fn, noargs); + } + + private: + LLVMContext context_; + IRBuilder<> builder_; + unique_ptr module_; + + void compile_switch(const shared_ptr ast) { + switch (ast->tag) { + case "assignment"_: + compile_assignment(ast); + break; + case "call"_: + compile_call(ast); + break; + case "statements"_: + compile_statements(ast); + break; + case "if"_: + compile_if(ast); + break; + case "while"_: + compile_while(ast); + break; + case "out"_: + compile_out(ast); + break; + default: + compile_switch(ast->nodes[0]); + break; + } + } + + Value* compile_switch_value(const shared_ptr ast) { + switch (ast->tag) { + case "odd"_: + return compile_odd(ast); + case "compare"_: + return compile_compare(ast); + case "expression"_: + return compile_expression(ast); + case "ident"_: + return compile_ident(ast); + case "number"_: + return compile_number(ast); + default: + return compile_switch_value(ast->nodes[0]); + } + } + + void compile_libs() { + auto printfF = module_->getOrInsertFunction( + "printf", + FunctionType::get(builder_.getInt32Ty(), + PointerType::get(builder_.getInt8Ty(), 0), true)); + + auto outF = cast(module_->getOrInsertFunction( + "out", builder_.getVoidTy(), builder_.getInt32Ty(), nullptr)); + { + auto BB = BasicBlock::Create(context_, "entry", outF); + builder_.SetInsertPoint(BB); + + auto val = &*outF->arg_begin(); + + auto fmt = builder_.CreateGlobalStringPtr("%d\n"); + std::vector args = {fmt, val}; + builder_.CreateCall(printfF, args); + + builder_.CreateRetVoid(); + } + } + + void compile_program(const shared_ptr ast) { + auto fn = cast(module_->getOrInsertFunction( + "__main__", builder_.getVoidTy(), nullptr)); + { + auto BB = BasicBlock::Create(context_, "entry", fn); + builder_.SetInsertPoint(BB); + compile_block(ast->nodes[0], true); + builder_.CreateRetVoid(); + } + } + + void compile_block(const shared_ptr ast, bool top) { + compile_const(ast->nodes[0], top); + compile_var(ast->nodes[1], top); + compile_procedure(ast->nodes[2]); + compile_statement(ast->nodes[3]); + } + + void compile_const(const shared_ptr ast, bool top) { + for (auto i = 0u; i < ast->nodes.size(); i += 2) { + auto ident = ast->nodes[i]->token; + auto number = stoi(ast->nodes[i + 1]->token); + + if (top) { + auto gv = cast( + module_->getOrInsertGlobal(ident, builder_.getInt32Ty())); + gv->setAlignment(4); + gv->setInitializer(builder_.getInt32(number)); + } else { + auto alloca = + builder_.CreateAlloca(builder_.getInt32Ty(), nullptr, ident); + builder_.CreateStore(builder_.getInt32(number), alloca); + } + } + } + + void compile_var(const shared_ptr ast, bool top) { + for (const auto node : ast->nodes) { + if (top) { + auto gv = cast( + module_->getOrInsertGlobal(node->token, builder_.getInt32Ty())); + gv->setAlignment(4); + gv->setInitializer(builder_.getInt32(0)); + } else { + builder_.CreateAlloca(builder_.getInt32Ty(), nullptr, node->token); + } + } + } + + void compile_procedure(const shared_ptr ast) { + for (auto i = 0u; i < ast->nodes.size(); i += 2) { + auto ident = ast->nodes[i]->token; + auto block = ast->nodes[i + 1]; + + auto fn = cast( + module_->getOrInsertFunction(ident, builder_.getVoidTy(), nullptr)); + { + auto prevBB = builder_.GetInsertBlock(); + auto BB = BasicBlock::Create(context_, "entry", fn); + builder_.SetInsertPoint(BB); + compile_block(block, false); + builder_.CreateRetVoid(); + builder_.SetInsertPoint(prevBB); + } + } + } + + void compile_statement(const shared_ptr ast) { + if (!ast->nodes.empty()) { + compile_switch(ast->nodes[0]); + } + } + + void compile_assignment(const shared_ptr ast) { + auto name = ast->nodes[0]->token; + + auto fn = builder_.GetInsertBlock()->getParent(); + auto tbl = fn->getValueSymbolTable(); + auto var = tbl->lookup(name); + if (!var) { + var = module_->getGlobalVariable(name); + } + + auto val = compile_expression(ast->nodes[1]); + builder_.CreateStore(val, var); + } + + void compile_call(const shared_ptr ast) { + auto fn = module_->getFunction(ast->nodes[0]->token); + builder_.CreateCall(fn); + } + + void compile_statements(const shared_ptr ast) { + for (auto node : ast->nodes) { + compile_statement(node); + } + } + + void compile_if(const shared_ptr ast) { + auto cond = compile_condition(ast->nodes[0]); + + auto fn = builder_.GetInsertBlock()->getParent(); + auto ifThen = BasicBlock::Create(context_, "if.then", fn); + auto ifEnd = BasicBlock::Create(context_, "if.end"); + + builder_.CreateCondBr(cond, ifThen, ifEnd); + + builder_.SetInsertPoint(ifThen); + compile_statement(ast->nodes[1]); + builder_.CreateBr(ifEnd); + + fn->getBasicBlockList().push_back(ifEnd); + builder_.SetInsertPoint(ifEnd); + } + + void compile_while(const shared_ptr ast) { + auto whileCond = BasicBlock::Create(context_, "while.cond"); + builder_.CreateBr(whileCond); + + auto fn = builder_.GetInsertBlock()->getParent(); + fn->getBasicBlockList().push_back(whileCond); + builder_.SetInsertPoint(whileCond); + + auto cond = compile_condition(ast->nodes[0]); + + auto whileBody = BasicBlock::Create(context_, "while.body", fn); + auto whileEnd = BasicBlock::Create(context_, "while.end"); + builder_.CreateCondBr(cond, whileBody, whileEnd); + + builder_.SetInsertPoint(whileBody); + compile_statement(ast->nodes[1]); + + builder_.CreateBr(whileCond); + + fn->getBasicBlockList().push_back(whileEnd); + builder_.SetInsertPoint(whileEnd); + } + + Value* compile_condition(const shared_ptr ast) { + return compile_switch_value(ast->nodes[0]); + } + + Value* compile_odd(const shared_ptr ast) { + auto val = compile_expression(ast->nodes[0]); + return builder_.CreateICmpNE(val, builder_.getInt32(0), "icmpne"); + } + + Value* compile_compare(const shared_ptr ast) { + auto lhs = compile_expression(ast->nodes[0]); + auto rhs = compile_expression(ast->nodes[2]); + + const auto& ope = ast->nodes[1]->token; + switch (ope[0]) { + case '=': + return builder_.CreateICmpEQ(lhs, rhs, "icmpeq"); + case '#': + return builder_.CreateICmpNE(lhs, rhs, "icmpne"); + case '<': + if (ope.size() == 1) { + return builder_.CreateICmpSLT(lhs, rhs, "icmpslt"); + } + // '<=' + return builder_.CreateICmpSLE(lhs, rhs, "icmpsle"); + case '>': + if (ope.size() == 1) { + return builder_.CreateICmpSGT(lhs, rhs, "icmpsgt"); + } + // '>=' + return builder_.CreateICmpSGE(lhs, rhs, "icmpsge"); + } + return nullptr; + } + + void compile_out(const shared_ptr ast) { + auto val = compile_expression(ast->nodes[0]); + auto outF = module_->getFunction("out"); + builder_.CreateCall(outF, val); + } + + Value* compile_expression(const shared_ptr ast) { + const auto& nodes = ast->nodes; + + auto sign = nodes[0]->token; + auto negative = !(sign.empty() || sign == "+"); + + auto val = compile_term(nodes[1]); + if (negative) { + val = builder_.CreateNeg(val, "negative"); + } + + for (auto i = 2u; i < nodes.size(); i += 2) { + auto ope = nodes[i + 0]->token[0]; + auto rval = compile_term(nodes[i + 1]); + switch (ope) { + case '+': + val = builder_.CreateAdd(val, rval, "add"); + break; + case '-': + val = builder_.CreateSub(val, rval, "sub"); + break; + } + } + return val; + } + + Value* compile_term(const shared_ptr ast) { + const auto& nodes = ast->nodes; + auto val = compile_factor(nodes[0]); + for (auto i = 1u; i < nodes.size(); i += 2) { + auto ope = nodes[i + 0]->token[0]; + auto rval = compile_switch_value(nodes[i + 1]); + switch (ope) { + case '*': + val = builder_.CreateMul(val, rval, "mul"); + break; + case '/': { + // TODO: Zero devide error? + // auto ret = builder_.CreateICmpEQ(rval, builder_.getInt32(0), + // "icmpeq"); + // if (!ret) { + // throw_runtime_error(ast, "divide by 0 error"); + // } + val = builder_.CreateSDiv(val, rval, "div"); + break; + } + } + } + return val; + } + + Value* compile_factor(const shared_ptr ast) { + return compile_switch_value(ast->nodes[0]); + } + + Value* compile_ident(const shared_ptr ast) { + auto name = ast->token; + + auto fn = builder_.GetInsertBlock()->getParent(); + auto tbl = fn->getValueSymbolTable(); + auto var = tbl->lookup(name); + if (!var) { + var = module_->getGlobalVariable(name); + } + + return builder_.CreateLoad(var); + } + + Value* compile_number(const shared_ptr ast) { + return ConstantInt::getIntegerValue(builder_.getInt32Ty(), + APInt(32, ast->token, 10)); + } +}; + /* * Main */ @@ -521,12 +878,31 @@ int main(int argc, const char** argv) { // Parse the source and make an AST shared_ptr ast; if (parser.parse_n(source.data(), source.size(), ast, path)) { - if (argc > 2 && string("--ast") == argv[2]) { + bool opt_llvm = false; + bool opt_ast = false; + { + auto argi = 2; + while (argi < argc) { + if (string("--ast") == argv[argi]) { + opt_ast = true; + } else if (string("--llvm") == argv[argi]) { + opt_llvm = true; + } + argi++; + } + } + + if (opt_ast) { cout << ast_to_s(ast); } + try { SymbolTable::build_on_ast(ast); - Interpreter::exec(ast); + if (opt_llvm) { + LLVM::exec(ast); + } else { + Interpreter::exec(ast); + } } catch (const runtime_error& e) { cerr << e.what() << endl; } @@ -535,5 +911,3 @@ int main(int argc, const char** argv) { return -1; } - -// vim: et ts=2 sw=2 cin cino={1s ff=unix diff --git a/language/pl0/pl0.sln b/language/pl0/pl0.sln deleted file mode 100644 index 2f45413..0000000 --- a/language/pl0/pl0.sln +++ /dev/null @@ -1,34 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.23107.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pl0", "pl0.vcxproj", "{6C5633BD-3CAE-498E-B0C6-ED90A1A99C47}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {F85B641A-7538-4809-8175-C528FF632CF6}.Debug|Win32.ActiveCfg = Debug|Win32 - {F85B641A-7538-4809-8175-C528FF632CF6}.Debug|Win32.Build.0 = Debug|Win32 - {F85B641A-7538-4809-8175-C528FF632CF6}.Release|Win32.ActiveCfg = Release|Win32 - {F85B641A-7538-4809-8175-C528FF632CF6}.Release|Win32.Build.0 = Release|Win32 - {1D09607B-E1C0-4D62-8AB4-9E2D2C2DC6E4}.Debug|Win32.ActiveCfg = Debug|Win32 - {1D09607B-E1C0-4D62-8AB4-9E2D2C2DC6E4}.Debug|Win32.Build.0 = Debug|Win32 - {1D09607B-E1C0-4D62-8AB4-9E2D2C2DC6E4}.Release|Win32.ActiveCfg = Release|Win32 - {1D09607B-E1C0-4D62-8AB4-9E2D2C2DC6E4}.Release|Win32.Build.0 = Release|Win32 - {E6146F73-3B4C-4D4C-BC55-148930954434}.Debug|Win32.ActiveCfg = Debug|Win32 - {E6146F73-3B4C-4D4C-BC55-148930954434}.Debug|Win32.Build.0 = Debug|Win32 - {E6146F73-3B4C-4D4C-BC55-148930954434}.Release|Win32.ActiveCfg = Release|Win32 - {E6146F73-3B4C-4D4C-BC55-148930954434}.Release|Win32.Build.0 = Release|Win32 - {6C5633BD-3CAE-498E-B0C6-ED90A1A99C47}.Debug|Win32.ActiveCfg = Debug|Win32 - {6C5633BD-3CAE-498E-B0C6-ED90A1A99C47}.Debug|Win32.Build.0 = Debug|Win32 - {6C5633BD-3CAE-498E-B0C6-ED90A1A99C47}.Release|Win32.ActiveCfg = Release|Win32 - {6C5633BD-3CAE-498E-B0C6-ED90A1A99C47}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/language/pl0/pl0.vcxproj b/language/pl0/pl0.vcxproj deleted file mode 100644 index 48be9c9..0000000 --- a/language/pl0/pl0.vcxproj +++ /dev/null @@ -1,92 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - - - - - - - {6C5633BD-3CAE-498E-B0C6-ED90A1A99C47} - Win32Proj - sample - pl0 - - - - Application - true - Unicode - v140 - - - Application - false - true - Unicode - v140 - - - - - - - - - - - - - true - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - ../.. - - - Console - true - Ws2_32.lib;%(AdditionalDependencies) - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - ../.. - - - Console - true - true - true - Ws2_32.lib;%(AdditionalDependencies) - - - - - - \ No newline at end of file diff --git a/language/pl0/samples/fib.pas b/language/pl0/samples/fib.pas index f21e931..ece16cb 100644 --- a/language/pl0/samples/fib.pas +++ b/language/pl0/samples/fib.pas @@ -20,7 +20,7 @@ END; BEGIN i := 0; - WHILE i < 30 DO BEGIN + WHILE i < 25 DO BEGIN x := i; CALL fib; write i; diff --git a/language/pl0/samples/square.pas b/language/pl0/samples/square.pas index e3759b4..2b0173a 100644 --- a/language/pl0/samples/square.pas +++ b/language/pl0/samples/square.pas @@ -3,7 +3,7 @@ VAR x, squ; PROCEDURE square; BEGIN - squ:= x * x + squ := x * x END; BEGIN