1 module d.llvm.evaluator;
2 
3 import d.llvm.codegen;
4 
5 import d.ir.expression;
6 
7 import d.semantic.evaluator;
8 
9 import util.visitor;
10 
11 import llvm.c.core;
12 import llvm.c.executionEngine;
13 
14 // In order to JIT, we redirect some call from libsdrt to druntime.
15 extern(C) {
16 	/**
17 	 * Memory allocation, forward to GC
18 	 */
19 	void* __sd_gc_tl_malloc(size_t size) {
20 		import core.memory;
21 		return GC.malloc(size);
22 	}
23 	
24 	void* __sd_gc_tl_array_alloc(size_t size) {
25 		import core.memory;
26 		return GC.malloc(size);
27 	}
28 	
29 	/**
30 	 * Forward bound check routines.
31 	 */
32 	void _d_arraybounds(string, int);
33 	void __sd_array_outofbounds(string file, int line) {
34 		_d_arraybounds(file, line);
35 	}
36 	
37 	/**
38 	 * Forward contract routines.
39 	 */
40 	void _d_assert(string, int);
41 	void __sd_assert_fail(string file, int line) {
42 		_d_assert(file, line);
43 	}
44 	
45 	void _d_assert_msg(string, string, int);
46 	void __sd_assert_fail_msg(string msg, string file, int line) {
47 		_d_assert_msg(msg, file, line);
48 	}
49 	
50 	/**
51 	 * Exception facilities are using a subset of libsdrt compiled with
52 	 * DMD called libsdmd. We need to anchor some functions here.
53 	 */
54 	void __sd_eh_throw(void*);
55 	immutable __anchor_sd_eh_throw = &__sd_eh_throw;
56 }
57 
58 final class LLVMEvaluator : Evaluator {
59 	private CodeGen pass;
60 	alias pass this;
61 	
62 	this(CodeGen pass) {
63 		this.pass = pass;
64 	}
65 	
66 	CompileTimeExpression evaluate(Expression e) {
67 		if (auto ce = cast(CompileTimeExpression) e) {
68 			return ce;
69 		}
70 		
71 		// We agressively JIT all CTFE
72 		return jit!(function CompileTimeExpression(
73 			CodeGen pass,
74 			Expression e,
75 			void[] p,
76 		) {
77 			return JitRepacker(pass, e.location, p).visit(e.type);
78 		})(e);
79 	}
80 	
81 	ulong evalIntegral(Expression e) in {
82 		auto t = e.type.getCanonical();
83 		while (t.kind = TypeKing.Enum) {
84 			t = t.denum.type.getCanonical();
85 		}
86 		
87 		assert(t.kind == TypeKind.Builtin);
88 		
89 		auto bt = t.builtin;
90 		assert(isIntegral(bt) || bt == BuiltinType.Bool);
91 	} do {
92 		return jit!(function ulong(ulong r) {
93 			return r;
94 		}, JitReturn.Direct)(e);
95 	}
96 	
97 	string evalString(Expression e) in {
98 		auto t = e.type.getCanonical();
99 		assert(t.kind = TypeKind.Slice);
100 		
101 		auto et = t.element.getCanonical();
102 		assert(et.builtin = BuiltinType.Char);
103 	} do {
104 		return jit!(function string(CodeGen pass, Expression e, void[] p) in {
105 			assert(p.length == string.sizeof);
106 		} do {
107 			auto s = *(cast(string*) p.ptr);
108 			return s.idup;
109 		})(e);
110 	}
111 	
112 	private auto jit(
113 		alias handler,
114 		JitReturn R = JitReturn.Indirect,
115 	)(Expression e) {
116 		scope(failure) LLVMDumpModule(dmodule);
117 		
118 		// Create a global variable to hold the returned blob.
119 		import d.llvm.type;
120 		auto type = TypeGen(pass).visit(e.type);
121 		
122 		static if (R == JitReturn.Direct) {
123 			auto returnType = type;
124 		} else {
125 			auto buffer = LLVMAddGlobal(dmodule, type, "__ctBuf");
126 			scope(exit) LLVMDeleteGlobal(buffer);
127 			
128 			LLVMSetInitializer(buffer, LLVMGetUndef(type));
129 			
130 			import llvm.c.target;
131 			auto size = LLVMStoreSizeOfType(targetData, type);
132 			auto returnType = LLVMInt64TypeInContext(llvmCtx);
133 		}
134 		
135 		// Generate function signature
136 		auto funType = LLVMFunctionType(returnType, null, 0, false);
137 		auto fun = LLVMAddFunction(dmodule, "__ctfe", funType);
138 		scope(exit) LLVMDeleteFunction(fun);
139 		
140 		// Generate function's body. Warning: horrible hack.
141 		import d.llvm.local;
142 		auto lg = LocalGen(pass, Mode.Eager);
143 		auto builder = lg.builder;
144 		
145 		auto bodyBB = LLVMAppendBasicBlockInContext(llvmCtx, fun, "");
146 		LLVMPositionBuilderAtEnd(builder, bodyBB);
147 		
148 		import d.llvm.expression;
149 		auto value = ExpressionGen(&lg).visit(e);
150 		
151 		static if (R == JitReturn.Direct) {
152 			LLVMBuildRet(builder, value);
153 		} else {
154 			LLVMBuildStore(builder, value, buffer);
155 			// FIXME This is 64bit only code.
156 			auto ptrToInt = LLVMBuildPtrToInt(
157 				builder,
158 				buffer,
159 				LLVMInt64TypeInContext(llvmCtx),
160 				"",
161 			);
162 			
163 			LLVMBuildRet(builder, ptrToInt);
164 		}
165 		
166 		checkModule();
167 		
168 		auto ee = createExecutionEngine(dmodule);
169 		scope(exit) destroyExecutionEngine(ee, dmodule);
170 		
171 		auto result = LLVMRunFunction(ee, fun, 0, null);
172 		scope(exit) LLVMDisposeGenericValue(result);
173 		
174 		static if (R == JitReturn.Direct) {
175 			return handler(LLVMGenericValueToInt(result, true));
176 		} else {
177 			// FIXME This only works for 64 bit platforms because the retval
178 			// of the "__ctfe" is specifically a i64. This is due to MCJIT
179 			// not supporting pointer return values directly at this time. 
180 			auto asInt = LLVMGenericValueToInt(result, false);
181 			return handler(pass, e, (cast(void*) asInt)[0 .. size]);
182 		}
183 	}
184 	
185 	import d.ir.symbol;
186 	auto createTestStub(Function f) {
187 		// Make sure the function we want to call is ready to go.
188 		import d.llvm.local;
189 		auto lg = LocalGen(pass, Mode.Eager);
190 		auto callee = lg.declare(f);
191 		
192 		// Generate function's body. Warning: horrible hack.
193 		auto builder = lg.builder;
194 		
195 		auto returnType = LLVMInt64TypeInContext(llvmCtx);
196 		auto funType = LLVMFunctionType(returnType, null, 0, false);
197 		auto fun = LLVMAddFunction(dmodule, "__unittest", funType);
198 		
199 		// Personality function to handle exceptions.
200 		LLVMSetPersonalityFn(fun, lg.declare(pass.object.getPersonality()));
201 		
202 		auto callBB = LLVMAppendBasicBlockInContext(llvmCtx, fun, "call");
203 		auto thenBB = LLVMAppendBasicBlockInContext(llvmCtx, fun, "then");
204 		auto lpBB = LLVMAppendBasicBlockInContext(llvmCtx, fun, "lp");
205 		
206 		LLVMPositionBuilderAtEnd(builder, callBB);
207 		LLVMBuildInvoke(builder, callee, null, 0, thenBB, lpBB, "");
208 		
209 		LLVMPositionBuilderAtEnd(builder, thenBB);
210 		LLVMBuildRet(builder, LLVMConstInt(returnType, 0, false));
211 		
212 		// Build the landing pad.
213 		LLVMTypeRef[2] lpTypes = [
214 			LLVMPointerType(LLVMInt8TypeInContext(llvmCtx), 0),
215 			LLVMInt32TypeInContext(llvmCtx),
216 		];
217 		
218 		auto lpType = LLVMStructTypeInContext(
219 			llvmCtx,
220 			lpTypes.ptr,
221 			lpTypes.length,
222 			false,
223 		);
224 		
225 		LLVMPositionBuilderAtEnd(builder, lpBB);
226 		auto landingPad = LLVMBuildLandingPad(
227 			builder,
228 			lpType,
229 			null,
230 			1,
231 			"",
232 		);
233 		
234 		auto typeofNull = LLVMPointerType(LLVMInt8TypeInContext(llvmCtx), 0);
235 		LLVMAddClause(landingPad, LLVMConstNull(typeofNull));
236 		
237 		// We don't care about cleanup for now.
238 		LLVMBuildRet(builder, LLVMConstInt(returnType, 1, false));
239 		
240 		return fun;
241 	}
242 }
243 
244 package:
245 auto createExecutionEngine(LLVMModuleRef dmodule) {
246 	char* errorPtr;
247 	LLVMExecutionEngineRef ee;
248 	auto creationError = LLVMCreateMCJITCompilerForModule(
249 		&ee,
250 		dmodule,
251 		null,
252 		0,
253 		&errorPtr,
254 	);
255 	
256 	if (creationError) {
257 		scope(exit) LLVMDisposeMessage(errorPtr);
258 		
259 		import core.stdc.string;
260 		auto error = errorPtr[0 .. strlen(errorPtr)].idup;
261 		
262 		import std.stdio;
263 		writeln(error);
264 		assert(0, "Cannot create execution engine ! Exiting...");
265 	}
266 	
267 	return ee;
268 }
269 
270 auto destroyExecutionEngine(LLVMExecutionEngineRef ee, LLVMModuleRef dmodule) {
271 	char* errorPtr;
272 	LLVMModuleRef outMod;
273 	auto removeError = LLVMRemoveModule(
274 		ee,
275 		dmodule,
276 		&outMod,
277 		&errorPtr,
278 	);
279 	
280 	if (removeError) {
281 		scope (exit) LLVMDisposeMessage(errorPtr);
282 		import core.stdc.string;
283 		auto error = errorPtr[0 .. strlen(errorPtr)].idup;
284 		
285 		import std.stdio;
286 		writeln(error);
287 		assert(
288 			0,
289 			"Cannot remove module from execution engine ! Exiting..."
290 		);
291 	}
292 	
293 	LLVMDisposeExecutionEngine(ee);
294 }
295 
296 private:
297 
298 enum JitReturn {
299 	Direct,
300 	Indirect,
301 }
302 
303 struct JitRepacker {
304 	CodeGen pass;
305 	alias pass this;
306 	
307 	import source.location;
308 	Location location;
309 	
310 	void[] p;
311 	
312 	this(CodeGen pass, Location location, void[] p) {
313 		this.pass = pass;
314 		this.p = p;
315 	}
316 	
317 	import d.ir.type, d.ir.symbol;
318 	CompileTimeExpression visit(Type t) in {
319 		import d.llvm.type, llvm.c.target;
320 		auto size = LLVMStoreSizeOfType(targetData, TypeGen(pass).visit(t));
321 		
322 		import std.conv;
323 		assert(
324 			size == p.length,
325 			"Buffer of length " ~ p.length.to!string() ~
326 				" when " ~ size.to!string() ~ " was expected"
327 		);
328 	} out(result) {
329 		// FIXME: This does not always pass now.
330 		// assert(result.type == t, "Result type do not match");
331 		assert(p.length == 0, "Remaining data in the buffer");
332 	} do {
333 		return t.accept(this);
334 	}
335 	
336 	T get(T)() {
337 		scope(exit) p = p[T.sizeof .. $];
338 		return *(cast(T*) p.ptr);
339 	}
340 	
341 	CompileTimeExpression visit(BuiltinType t) {
342 		ulong raw;
343 		switch(t) with(BuiltinType) {
344 			case Bool :
345 				return new BooleanLiteral(location, get!bool());
346 			
347 			case Byte, Ubyte:
348 				raw = get!ubyte();
349 				goto HandleIntegral;
350 			
351 			case Short, Ushort:
352 				raw = get!ushort();
353 				goto HandleIntegral;
354 			
355 			case Int, Uint:
356 				raw = get!uint();
357 				goto HandleIntegral;
358 			
359 			case Long, Ulong:
360 				raw = get!ulong();
361 				goto HandleIntegral;
362 			
363 			HandleIntegral:
364 				return new IntegerLiteral(location, raw, t);
365 			
366 			default:
367 				assert(0, "Not implemented");
368 		}
369 	}
370 	
371 	CompileTimeExpression visitPointerOf(Type t) {
372 		assert(0, "Not implemented");
373 	}
374 	
375 	CompileTimeExpression visitSliceOf(Type t) {
376 		if (
377 			t.kind == TypeKind.Builtin &&
378 			t.builtin == BuiltinType.Char &&
379 			t.qualifier == TypeQualifier.Immutable
380 		) {
381 			return new StringLiteral(location, get!string().idup);
382 		}
383 		
384 		assert(0, "Not Implemented.");
385 	}
386 	
387 	CompileTimeExpression visitArrayOf(uint size, Type t) {
388 		import d.llvm.type, llvm.c.target;
389 		uint elementSize = cast(uint) LLVMStoreSizeOfType(
390 			targetData,
391 			TypeGen(pass).visit(t),
392 		);
393 		
394 		CompileTimeExpression[] elements;
395 		elements.reserve(size);
396 		
397 		auto buf = p;
398 		uint start = 0;
399 		scope(exit) p = buf[start .. $];
400 		
401 		for (uint i = 0; i < size; i++) {
402 			uint end = start + elementSize;
403 			p = buf[start .. end];
404 			start = end;
405 			elements ~= visit(t);
406 		}
407 		
408 		return new CompileTimeTupleExpression(
409 			location,
410 			t.getArray(size),
411 			elements,
412 		);
413 	}
414 	
415 	CompileTimeExpression visit(Struct s) {
416 		import d.llvm.type;
417 		auto type = TypeGen(pass).visit(s);
418 		
419 		import llvm.c.target;
420 		auto size = LLVMStoreSizeOfType(targetData, type);
421 		
422 		auto buf = p;
423 		scope(exit) p = buf[size .. $];
424 		
425 		CompileTimeExpression[] elements;
426 		
427 		uint i = 0;
428 		foreach (m; s.members) {
429 			if (auto f = cast(Field) m) {
430 				scope(success) i++;
431 				
432 				assert(f.index == i, "fields are out of order");
433 				auto t = f.type;
434 				
435 				auto start = LLVMOffsetOfElement(targetData, type, i);
436 				auto elementType = LLVMStructGetTypeAtIndex(type, i);
437 				
438 				auto fieldSize = LLVMStoreSizeOfType(targetData, elementType);
439 				auto end = start + fieldSize;
440 				
441 				p = buf[start .. end];
442 				elements ~= visit(t);
443 			}
444 		}
445 		
446 		return new CompileTimeTupleExpression(location, Type.get(s), elements);
447 	}
448 	
449 	CompileTimeExpression visit(Class c) {
450 		assert(0, "Not Implemented.");
451 	}
452 	
453 	CompileTimeExpression visit(Enum e) {
454 		// TODO: build implicit cast.
455 		return visit(e.type);
456 	}
457 	
458 	CompileTimeExpression visit(TypeAlias a) {
459 		// TODO: build implicit cast.
460 		return visit(a.type);
461 	}
462 	
463 	CompileTimeExpression visit(Interface i) {
464 		assert(0, "Not Implemented.");
465 	}
466 	
467 	CompileTimeExpression visit(Union u) {
468 		assert(0, "Not Implemented.");
469 	}
470 	
471 	CompileTimeExpression visit(Function f) {
472 		assert(0, "Not Implemented.");
473 	}
474 	
475 	CompileTimeExpression visit(Type[] seq) {
476 		assert(0, "Not Implemented.");
477 	}
478 	
479 	CompileTimeExpression visit(FunctionType f) {
480 		assert(0, "Not Implemented.");
481 	}
482 	
483 	CompileTimeExpression visit(Pattern p) {
484 		assert(0, "Not implemented.");
485 	}
486 	
487 	import d.ir.error;
488 	CompileTimeExpression visit(CompileError e) {
489 		assert(0, "Not implemented.");
490 	}
491 }