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 }