Skip to content

Instantly share code, notes, and snippets.

@vivekvpandya
Created March 1, 2016 20:12
Show Gist options
  • Save vivekvpandya/7a9e2a0fab365ac4e663 to your computer and use it in GitHub Desktop.
Save vivekvpandya/7a9e2a0fab365ac4e663 to your computer and use it in GitHub Desktop.
module d.llvm.evaluator;
import d.llvm.codegen;
import d.ir.expression;
import d.semantic.evaluator;
import util.visitor;
import llvm.c.core;
import llvm.c.executionEngine;
import llvm.c.orcBinding;
import llvm.c.targetMachine;
// In order to JIT.
extern(C) void _d_assert(string, int);
extern(C) void _d_assert_msg(string, string, int);
extern(C) void _d_arraybounds(string, int);
extern(C) void* _d_allocmemory(size_t);
extern(C) void* __sd_array_alloc(size_t size) {
import core.memory;
return GC.malloc(size);
}
final class LLVMEvaluator : Evaluator {
private CodeGen pass;
LLVMTargetMachineRef targetMachine;
alias pass this;
this(CodeGen pass, LLVMTargetMachineRef targetMachine) {
this.pass = pass;
this.targetMachine = targetMachine;
}
CompileTimeExpression evaluate(Expression e) {
if (auto ce = cast(CompileTimeExpression) e) {
return ce;
}
// We agressively JIT all CTFE
return jit!(function CompileTimeExpression(
CodeGen pass,
Expression e,
void[] p,
) {
return JitRepacker(pass, e.location, p).visit(e.type);
})(e);
}
ulong evalIntegral(Expression e) in {
auto t = e.type.getCanonical();
while (t.kind = TypeKing.Enum) {
t = t.denum.type.getCanonical();
}
assert(t.kind == TypeKind.Builtin);
auto bt = t.builtin;
assert(isIntegral(bt) || bt == BuiltinType.Bool);
} body {
return jit!(function ulong(ulong r) {
return r;
},JitReturn.Direct)(e);
}
string evalString(Expression e) in {
auto t = e.type.getCanonical();
assert(t.kind = TypeKind.Slice);
auto et = t.element.getCanonical();
assert(et.builtin = BuiltinType.Char);
} body {
return jit!(function string(CodeGen pass, Expression e, void[] p) in {
assert(p.length == string.sizeof);
} body {
auto s = *(cast(string*) p.ptr);
return s.idup;
})(e);
}
private auto jit(
alias handler,
JitReturn R = JitReturn.Indirect,
)(Expression e) {
scope(failure) LLVMDumpModule(dmodule);
// Create a global variable to hold the returned blob.
import d.llvm.type;
auto type = TypeGen(pass).visit(e.type);
static if (R == JitReturn.Direct) {
auto returnType = type;
} else {
auto buffer = LLVMAddGlobal(dmodule, type, "__ctBuf");
scope(exit) LLVMDeleteGlobal(buffer);
LLVMSetInitializer(buffer, LLVMGetUndef(type));
import llvm.c.target;
auto size = LLVMStoreSizeOfType(targetData, type);
auto returnType = LLVMInt64TypeInContext(llvmCtx);
}
// Generate function signature
auto funType = LLVMFunctionType(returnType, null, 0, false);
auto fun = LLVMAddFunction(dmodule, "__ctfe", funType);
scope(exit) LLVMDeleteFunction(fun);
// Generate function's body. Warning: horrible hack.
import d.llvm.local;
auto lg = LocalGen(pass, Mode.Lazy);
auto builder = lg.builder;
auto bodyBB = LLVMAppendBasicBlockInContext(llvmCtx, fun, "");
LLVMPositionBuilderAtEnd(builder, bodyBB);
import d.llvm.expression;
auto value = ExpressionGen(&lg).visit(e);
static if (R == JitReturn.Direct) {
LLVMBuildRet(builder, value);
} else {
LLVMBuildStore(builder, value, buffer);
// FIXME This is 64bit only code.
auto ptrToInt = LLVMBuildPtrToInt(
builder,
buffer,
LLVMInt64TypeInContext(llvmCtx),
"",
);
LLVMBuildRet(builder, ptrToInt);
}
checkModule();
LLVMOrcJITStackRef OrcStakcRef = LLVMOrcCreateInstance(targetMachine);
LLVMOrcModuleHandle ModuleHandle = LLVMOrcAddLazilyCompiledIR(OrcStakcRef, dmodule, null, null); // no external symbol resolver for now.
LLVMOrcTargetAddress pointerToCode = LLVMOrcGetSymbolAddress(OrcStakcRef,"__ctfe");
//auto executionEngine = createExecutionEngine(dmodule);
//scope(exit) {
// char* errorPtr;
// LLVMModuleRef outMod;
// auto removeError = LLVMRemoveModule(
// executionEngine,
// dmodule,
// &outMod,
// &errorPtr,
// );
// if (removeError) {
// scope (exit) LLVMDisposeMessage(errorPtr);
// import std.c.string;
// auto error = errorPtr[0 .. strlen(errorPtr)].idup;
// import std.stdio;
// writeln(error);
// assert(
// 0,
// "Cannot remove module from execution engine ! Exiting..."
// );
// }
// LLVMDisposeExecutionEngine(executionEngine);
//}
//auto result = LLVMRunFunction(executionEngine, fun, 0, null);
import std.stdio;
writeln("\n----------------------- Horrible ! It has come so far.------------------------ \n");
//funType function() fp;
//fp = cast(funType function()) pointerToCode;
//auto result = fp();
int function() fp;
fp = cast(int function()) pointerToCode;
auto result = fp();
//scope(exit) LLVMDisposeGenericValue(result);
static if (R == JitReturn.Direct) {
//return handler(LLVMGenericValueToInt(result, true));
return handler(result);
} else {
// FIXME This only works for 64 bit platforms because the retval
// of the "__ctfe" is specifically a i64. This is due to MCJIT
// not supporting pointer return values directly at this time.
//auto asInt = LLVMGenericValueToInt(result, false);
//return handler(pass, e, (cast(void*) asInt)[0 .. size]);
return handler(pass, e, (cast(void*) result)[0 .. size]);
}
}
private auto createExecutionEngine(LLVMModuleRef dmodule) {
char* errorPtr;
LLVMExecutionEngineRef executionEngine;
auto creationError = LLVMCreateMCJITCompilerForModule(
&executionEngine,
dmodule,
null,
0,
&errorPtr,
);
if (creationError) {
scope(exit) LLVMDisposeMessage(errorPtr);
import std.c.string;
auto error = errorPtr[0 .. strlen(errorPtr)].idup;
import std.stdio;
writeln(error);
assert(0, "Cannot create execution engine ! Exiting...");
}
return executionEngine;
}
}
private:
enum JitReturn {
Direct,
Indirect,
}
struct JitRepacker {
CodeGen pass;
alias pass this;
import d.context.location;
Location location;
void[] p;
this(CodeGen pass, Location location, void[] p) {
this.pass = pass;
this.p = p;
}
import d.ir.type, d.ir.symbol;
CompileTimeExpression visit(Type t) in {
import d.llvm.type, llvm.c.target;
auto size = LLVMStoreSizeOfType(targetData, TypeGen(pass).visit(t));
import std.conv;
assert(
size == p.length,
"Buffer of length " ~ p.length.to!string() ~
" when " ~ size.to!string() ~ " was expected"
);
} out(result) {
// FIXME: This does not always pass now.
// assert(result.type == t, "Result type do not match");
assert(p.length == 0, "Remaining data in the buffer");
} body {
return t.accept(this);
}
T get(T)() {
scope(exit) p = p[T.sizeof .. $];
return *(cast(T*) p.ptr);
}
CompileTimeExpression visit(BuiltinType t) {
ulong raw;
switch(t) with(BuiltinType) {
case Bool :
return new BooleanLiteral(location, get!bool());
case Byte, Ubyte:
raw = get!ubyte();
goto HandleIntegral;
case Short, Ushort:
raw = get!ushort();
goto HandleIntegral;
case Int, Uint:
raw = get!uint();
goto HandleIntegral;
case Long, Ulong:
raw = get!ulong();
goto HandleIntegral;
HandleIntegral:
return new IntegerLiteral(location, raw, t);
default:
assert(0, "Not implemented");
}
}
CompileTimeExpression visitPointerOf(Type t) {
assert(0, "Not implemented");
}
CompileTimeExpression visitSliceOf(Type t) {
if (
t.kind == TypeKind.Builtin &&
t.builtin == BuiltinType.Char &&
t.qualifier == TypeQualifier.Immutable
) {
return new StringLiteral(location, get!string().idup);
}
assert(0, "Not Implemented.");
}
CompileTimeExpression visitArrayOf(uint size, Type t) {
import d.llvm.type, llvm.c.target;
uint elementSize = cast(uint) LLVMStoreSizeOfType(
targetData,
TypeGen(pass).visit(t),
);
CompileTimeExpression[] elements;
elements.reserve(size);
auto buf = p;
uint start = 0;
scope(exit) p = buf[start .. $];
for (uint i = 0; i < size; i++) {
uint end = start + elementSize;
p = buf[start .. end];
start = end;
elements ~= visit(t);
}
return new CompileTimeTupleExpression(
location,
t.getArray(size),
elements,
);
}
CompileTimeExpression visit(Struct s) {
import d.llvm.type;
auto type = TypeGen(pass).visit(s);
import llvm.c.target;
auto size = LLVMStoreSizeOfType(targetData, type);
auto buf = p;
scope(exit) p = buf[size .. $];
CompileTimeExpression[] elements;
uint i = 0;
foreach (m; s.members) {
if (auto f = cast(Field) m) {
scope(success) i++;
assert(f.index == i, "fields are out of order");
auto t = f.type;
auto start = LLVMOffsetOfElement(targetData, type, i);
auto elementType = LLVMStructGetTypeAtIndex(type, i);
auto fieldSize = LLVMStoreSizeOfType(targetData, elementType);
auto end = start + fieldSize;
p = buf[start .. end];
elements ~= visit(t);
}
}
return new CompileTimeTupleExpression(location, Type.get(s), elements);
}
CompileTimeExpression visit(Class c) {
assert(0, "Not Implemented.");
}
CompileTimeExpression visit(Enum e) {
// TODO: build implicit cast.
return visit(e.type);
}
CompileTimeExpression visit(TypeAlias a) {
// TODO: build implicit cast.
return visit(a.type);
}
CompileTimeExpression visit(Interface i) {
assert(0, "Not Implemented.");
}
CompileTimeExpression visit(Union u) {
assert(0, "Not Implemented.");
}
CompileTimeExpression visit(Function f) {
assert(0, "Not Implemented.");
}
CompileTimeExpression visit(Type[] seq) {
assert(0, "Not Implemented.");
}
CompileTimeExpression visit(FunctionType f) {
assert(0, "Not Implemented.");
}
CompileTimeExpression visit(TypeTemplateParameter p) {
assert(0, "Not implemented.");
}
import d.ir.error;
CompileTimeExpression visit(CompileError e) {
assert(0, "Not implemented.");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment