Skip to content

Instantly share code, notes, and snippets.

@kinoh
Created August 3, 2014 12:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kinoh/a8ef59bc70a6ac29fc5c to your computer and use it in GitHub Desktop.
Save kinoh/a8ef59bc70a6ac29fc5c to your computer and use it in GitHub Desktop.
LLVM & Irony in C#
using System;
using System.Collections.Generic;
using System.Linq;
using Irony.Parsing;
using LLVM;
namespace KaleidoScope
{
[Language("KaleidoScope")]
public class KaleidoScopeGrammar : Grammar
{
public KaleidoScopeGrammar()
: base(true)
{
var number = new NumberLiteral("NumberExpr");
var variable = new IdentifierTerminal("VariableExpr");
var ident = new IdentifierTerminal("IdentifierExpr");
var binary = new NonTerminal("BinaryExpr");
var call = new NonTerminal("CallExpr");
var value = new NonTerminal("ValueExpr");
var expr = new NonTerminal("Expr");
var prototype = new NonTerminal("Prototype");
var def = new NonTerminal("Definition");
var external = new NonTerminal("External");
var top = new NonTerminal("TopLevel");
var paren = new NonTerminal("ParenExpr");
var args = new NonTerminal("ArgList");
var ids = new NonTerminal("IdentList");
var binOpr = new NonTerminal("BinaryOperator");
number.DefaultFloatType = TypeCode.Double;
paren.Rule = "(" + expr + ")";
value.Rule = variable | call;
args.Rule = MakeListRule(args, ToTerm(","), expr);
call.Rule = variable + "(" + args + ")";
expr.Rule = value | number | paren | binary;
binOpr.Rule = ToTerm("+") | "-" | "*" | "/";
binary.Rule = expr + binOpr + expr;
ids.Rule = MakeListRule(ids, ToTerm(","), ident);
prototype.Rule = ident + "(" + ids + ")";
def.Rule = ToTerm("def") + prototype + expr;
external.Rule = ToTerm("extern") + prototype;
top.Rule = def | external | expr;
this.Root = top;
this.MarkPunctuation(",", "(", ")");
this.RegisterOperators(2, "*", "/");
this.RegisterOperators(1, "+", "-");
}
}
class CodeGenerator
{
IRBuilder builder;
Module module;
static Dictionary<string, Value> namedVars;
public CodeGenerator()
{
var context = new LLVMContext();
builder = new IRBuilder(context);
module = new Module("my cool jit", context);
namedVars = new Dictionary<string, Value>();
}
public void Dump()
{
module.dump();
}
public Value Generate(ParseTreeNode node)
{
return generate(node);
}
Value errorValue(string message)
{
Console.WriteLine(message);
return null;
}
Value generate(ParseTreeNode node)
{
switch (node.Term.Name)
{
case "TopLevel":
case "Expr":
case "ValueExpr":
case "ParenExpr":
return generate(node.ChildNodes[0]);
case "NumberExpr":
return ConstantFP.get(builder.getDoubleTy(), double.Parse(node.Token.Text));
case "VariableExpr":
{
string name = node.Token.Text;
if (!namedVars.ContainsKey(name))
return errorValue("Unknown variable name");
return namedVars[name];
}
case "BinaryExpr":
{
Value lhs = generate(node.ChildNodes[0]);
Value rhs = generate(node.ChildNodes[2]);
switch (node.ChildNodes[1].ChildNodes[0].Token.Text)
{
case "+": return builder.CreateFAdd(lhs, rhs, "addtmp");
case "-": return builder.CreateFSub(lhs, rhs, "subtmp");
case "*": return builder.CreateFMul(lhs, rhs, "multmp");
case "/": return builder.CreateFDiv(lhs, rhs, "divtmp");
case "<":
var r = builder.CreateFCmpULT(lhs, rhs, "cmptmp");
return builder.CreateUIToFP(r, builder.getDoubleTy());
default:
return errorValue("Invalid binary operator");
}
}
case "CallExpr":
{
string name = node.ChildNodes[0].Token.Text;
var args = node.ChildNodes[1].ChildNodes;
Function func = module.getFunction(name);
if (func == null)
return errorValue("Unknown function referenced");
if (func.arg_size() != args.Count)
return errorValue("Incorrect # arguments passed");
return builder.CreateCall(func,
args.Select(a => generate(a)).ToArray(),
"calltmp");
}
case "Prototype":
return generatePrototype(node);
case "External":
return generatePrototype(node.ChildNodes[1]);
case "Definition":
{
namedVars.Clear();
Function f = generatePrototype(node.ChildNodes[1]);
if (f == null)
return null;
BasicBlock block = BasicBlock.Create(builder.getContext(), "entry", f);
builder.SetInsertPoint(block);
Value ret = generate(node.ChildNodes[2]);
if (ret == null)
{
f.eraseFromParent();
return null;
}
builder.CreateRet(ret);
Verifier.verifyFunction(f);
return f;
}
}
throw new NotImplementedException();
}
Function generatePrototype(ParseTreeNode node)
{
if (node.Term.Name != "Prototype")
throw new ArgumentException();
string name = node.ChildNodes[0].Token.Text;
var args = node.ChildNodes[1].ChildNodes;
FunctionType type = FunctionType.get(
builder.getDoubleTy(),
args.Select(a => builder.getDoubleTy()).ToArray(),
false);
Function f = Function.Create(type, GlobalValue.LinkageTypes.ExternalLinkage, name, module);
if (f.getName() != name)
{
f.eraseFromParent();
f = module.getFunction(name);
if (!f.empty())
{
Console.WriteLine("Redifinition of function");
return null;
}
if (f.arg_size() != args.Count)
{
Console.WriteLine("Redefinition of function with different # args");
return null;
}
}
int i = 0;
f.getArgumentList().ForEach(a =>
{
var n = args[i].Token.Text;
a.setName(n);
namedVars.Add(n, a);
i++;
});
return f;
}
}
class Program
{
static void Main(string[] args)
{
var grammar = new KaleidoScopeGrammar();
var parser = new Parser(grammar);
var codegen = new CodeGenerator();
string str;
Console.Write("ready> ");
while ((str = Console.ReadLine()).Length > 0)
{
var tree = parser.Parse(str);
if (tree.Status == ParseTreeStatus.Parsed)
{
Console.WriteLine("Parsed : {0}", tree.Root.ChildNodes[0]);
var v = codegen.Generate(tree.Root);
}
else
Console.WriteLine("Error : ", string.Join("\n", tree.ParserMessages));
Console.Write("ready> ");
}
Console.WriteLine("Generated :");
codegen.Dump();
Console.ReadKey();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment