Created
August 3, 2014 12:44
-
-
Save kinoh/a8ef59bc70a6ac29fc5c to your computer and use it in GitHub Desktop.
LLVM & Irony in C#
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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