Added LLVM support to PL/0 sample

This commit is contained in:
yhirose 2017-08-07 23:43:16 -04:00
parent 7c24c30104
commit 7c1e1751d9
7 changed files with 387 additions and 150 deletions

View File

@ -69,5 +69,4 @@ add_subdirectory(example)
if(NOT MSVC)
add_subdirectory(lint)
add_subdirectory(language/pl0)
endif()

View File

@ -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)

View File

@ -9,9 +9,16 @@
#include <fstream>
#include <iostream>
#include <sstream>
#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<AstPL0> ast,
shared_ptr<SymbolScope> 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<SymbolScope> scope, shared_ptr<Environment> outer)
@ -263,9 +271,6 @@ struct Environment {
map<string, int> variables;
};
/*
* Interpreter
*/
struct Interpreter {
static void exec(const shared_ptr<AstPL0> ast,
shared_ptr<Environment> env = nullptr) {
@ -312,8 +317,7 @@ struct Interpreter {
static void exec_statement(const shared_ptr<AstPL0> ast,
shared_ptr<Environment> 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<Module>("pl0", context_);
}
void compile(const shared_ptr<AstPL0> ast) {
InitializeNativeTarget();
InitializeNativeTargetAsmPrinter();
compile_libs();
compile_program(ast);
}
void dump() { module_->dump(); }
static void exec(const shared_ptr<AstPL0> ast) {
LLVM compiler;
compiler.compile(ast);
// compiler.dump();
auto EE = EngineBuilder(std::move(compiler.module_)).create();
std::unique_ptr<ExecutionEngine> ExecutionEngineOwner(EE);
std::vector<GenericValue> noargs;
auto fn = EE->FindFunctionNamed("__main__");
auto ret = EE->runFunction(fn, noargs);
}
private:
LLVMContext context_;
IRBuilder<> builder_;
unique_ptr<Module> module_;
void compile_switch(const shared_ptr<AstPL0> 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<AstPL0> 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<Function>(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<Value*> args = {fmt, val};
builder_.CreateCall(printfF, args);
builder_.CreateRetVoid();
}
}
void compile_program(const shared_ptr<AstPL0> ast) {
auto fn = cast<Function>(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<AstPL0> 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<AstPL0> 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<GlobalVariable>(
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<AstPL0> ast, bool top) {
for (const auto node : ast->nodes) {
if (top) {
auto gv = cast<GlobalVariable>(
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<AstPL0> 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<Function>(
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<AstPL0> ast) {
if (!ast->nodes.empty()) {
compile_switch(ast->nodes[0]);
}
}
void compile_assignment(const shared_ptr<AstPL0> 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<AstPL0> ast) {
auto fn = module_->getFunction(ast->nodes[0]->token);
builder_.CreateCall(fn);
}
void compile_statements(const shared_ptr<AstPL0> ast) {
for (auto node : ast->nodes) {
compile_statement(node);
}
}
void compile_if(const shared_ptr<AstPL0> 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<AstPL0> 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<AstPL0> ast) {
return compile_switch_value(ast->nodes[0]);
}
Value* compile_odd(const shared_ptr<AstPL0> ast) {
auto val = compile_expression(ast->nodes[0]);
return builder_.CreateICmpNE(val, builder_.getInt32(0), "icmpne");
}
Value* compile_compare(const shared_ptr<AstPL0> 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<AstPL0> ast) {
auto val = compile_expression(ast->nodes[0]);
auto outF = module_->getFunction("out");
builder_.CreateCall(outF, val);
}
Value* compile_expression(const shared_ptr<AstPL0> 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<AstPL0> 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<AstPL0> ast) {
return compile_switch_value(ast->nodes[0]);
}
Value* compile_ident(const shared_ptr<AstPL0> 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<AstPL0> 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<AstPL0> 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);
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

View File

@ -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

View File

@ -1,92 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\peglib.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="pl0.cc" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{6C5633BD-3CAE-498E-B0C6-ED90A1A99C47}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>sample</RootNamespace>
<ProjectName>pl0</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v140</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v140</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -20,7 +20,7 @@ END;
BEGIN
i := 0;
WHILE i < 30 DO BEGIN
WHILE i < 25 DO BEGIN
x := i;
CALL fib;
write i;