/* -*- mode: objc; coding: utf-8 -*- */ /* Toilet Lisp, a Common Lisp subset for the Étoilé runtime. * Copyright (C) 2008 Matthias Andreas Benkard. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #import "MLKDynamicContext.h" #import "MLKLLVMCompiler.h" #import "MLKPackage.h" #import "globals.h" #import "util.h" #import #import #import #import #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace llvm; static ExecutionEngine *execution_engine; static llvm::Module *module; static IRBuilder builder; static FunctionPassManager *fpm; static PointerType *PointerTy; static ModuleProvider *module_provider; static Constant *createGlobalStringPtr (const char *string) { Constant *(indices[2]); indices[0] = indices[1] = ConstantInt::get (Type::Int32Ty, 0); Constant *str = ConstantArray::get (string); Constant *str2 = new GlobalVariable (str->getType(), true, GlobalValue::InternalLinkage, str, "", module); Constant *ptr = ConstantExpr::getGetElementPtr (str2, indices, 2); return ptr; } @implementation MLKLLVMCompiler +(void) initialize { module = new llvm::Module ("MLKLLVMModule"); execution_engine = ExecutionEngine::create (module); PointerTy = PointerType::get(Type::Int8Ty, 0); module_provider = new ExistingModuleProvider (module); fpm = new FunctionPassManager (module_provider); fpm->add (new TargetData (*execution_engine->getTargetData())); //fpm->add (new TargetData (module)); fpm->add (createInstructionCombiningPass()); fpm->add (createReassociatePass()); fpm->add (createGVNPass()); // fpm->add (createVerifierPass()); //fpm->add (createLowerSetJmpPass()); //fpm->add (createRaiseAllocationsPass()); fpm->add (createCFGSimplificationPass()); fpm->add (createPromoteMemoryToRegisterPass()); //fpm->add (createGlobalOptimizerPass()); //fpm->add (createGlobalDCEPass()); //fpm->add (createFunctionInliningPass()); // Utilities. // fpm->add (createUnifyFunctionExitNodesPass()); } +(id) compile:(id)object inContext:(MLKLexicalContext *)context { NSAutoreleasePool *pool; pool = [[NSAutoreleasePool alloc] init]; Value *v = NULL; BasicBlock *block; std::vector noargs (0, Type::VoidTy); FunctionType *function_type = FunctionType::get (PointerTy, noargs, false); Function *function = Function::Create (function_type, Function::ExternalLinkage, "", module); id lambdaForm; id (*fn)(); MLKForm *form = [MLKForm formWithObject:object inContext:context forCompiler:self]; block = BasicBlock::Create ("entry", function); builder.SetInsertPoint (block); v = [self processForm:form]; builder.CreateRet (v); verifyFunction (*function); fpm->run (*function); // JIT-compile. fn = (id (*)()) execution_engine->getPointerToFunction (function); //module->dump(); NSLog (@"%p", fn); [pool release]; NSLog (@"Code compiled."); // Execute. lambdaForm = fn(); NSLog (@"Closure built."); return lambdaForm; } +(void) processTopLevelForm:(id)object { [self processTopLevelForm:object inMode:not_compile_time_mode]; } +(void) processTopLevelForm:(id)object inMode:(enum MLKProcessingMode)mode { //FIXME // If PROGN, do this... If EVAL-WHEN, do that... } +(id) eval:(id)object { return [self compile:object inContext:[MLKLexicalContext globalContext]]; } +(Value *) processForm:(MLKForm *)form { return [form processForLLVM]; } +(Value *) insertSelectorLookup:(NSString *)name { Constant *function = module->getOrInsertFunction ( #ifdef __NEXT_RUNTIME__ "sel_getUid", #else "sel_get_uid", #endif PointerTy, PointerTy, NULL); Constant *nameptr = createGlobalStringPtr ([name UTF8String]); return builder.CreateCall (function, nameptr, "selector"); } +(Value *) insertMethodCall:(NSString *)messageName onObject:(Value *)object withArgumentVector:(std::vector *)argv { return [self insertMethodCall:messageName onObject:object withArgumentVector:argv name:@""]; } +(Value *) insertVoidMethodCall:(NSString *)messageName onObject:(Value *)object withArgumentVector:(std::vector *)argv { return [self insertMethodCall:messageName onObject:object withArgumentVector:argv name:@"" returnType:(Type::VoidTy)]; } +(Value *) insertMethodCall:(NSString *)messageName onObject:(Value *)object withArgumentVector:(std::vector *)argv name:(NSString *)name { return [self insertMethodCall:messageName onObject:object withArgumentVector:argv name:@"" returnType:PointerTy]; } +(Value *) insertMethodCall:(NSString *)messageName onObject:(Value *)object withArgumentVector:(std::vector *)argv name:(NSString *)name returnType:(const Type *)returnType { std::vector argtypes (2, PointerTy); FunctionType *ftype = FunctionType::get (returnType, argtypes, true); Value *sel = [self insertSelectorLookup:messageName]; #ifdef __NEXT_RUNTIME__ Constant *function = module->getOrInsertFunction ("objc_msgSend", ftype); #else std::vector lookup_argtypes (2, PointerTy); FunctionType *lookup_ftype = FunctionType::get (PointerType::get (ftype, 0), lookup_argtypes, false); Constant *lookup_function = module->getOrInsertFunction ("objc_msg_lookup", lookup_ftype); Value *function = builder.CreateCall2 (lookup_function, object, sel, "method_impl"); #endif // XXX The following doesn't work. Why? // std::deque argd (*argv); // argd.push_front (sel); // argd.push_front (object); std::vector argd; argd.push_back (object); argd.push_back (sel); std::vector::iterator e; for (e = argv->begin(); e != argv->end(); e++) argd.push_back (*e); return builder.CreateCall (function, argd.begin(), argd.end()); } +(Value *) insertMethodCall:(NSString *)messageName onObject:(Value *)object withName:(NSString *)name { std::vector argv; return [self insertMethodCall:messageName onObject:object withArgumentVector:&argv name:name]; } +(Value *) insertMethodCall:(NSString *)messageName onObject:(Value *)object { return [self insertMethodCall:messageName onObject:object withName:@""]; } +(Value *) insertClassLookup:(NSString *)className { Constant *function = module->getOrInsertFunction ( #ifdef __NEXT_RUNTIME__ "objc_getClass", #else "objc_get_class", #endif PointerTy, PointerTy, NULL); const char *cname = [className UTF8String]; // Value *nameptr = builder.CreateGlobalStringPtr (cname, ""); Constant *nameptr = createGlobalStringPtr (cname); return builder.CreateCall (function, nameptr, cname); } +(void) insertTrace:(NSString *)message { Constant *function = module->getOrInsertFunction ("puts", Type::Int32Ty, PointerTy, NULL); builder.CreateCall (function, createGlobalStringPtr ([message UTF8String])); } @end @implementation MLKForm (MLKLLVMCompilation) -(Value *) processForLLVM { NSLog (@"WARNING: Unrecognised form type: %@", self); return NULL; } @end @implementation MLKProgNForm (MLKLLVMCompilation) -(Value *) processForLLVM { NSEnumerator *e = [_bodyForms objectEnumerator]; MLKForm *form; Value *value = ConstantPointerNull::get (PointerTy); while ((form = [e nextObject])) { value = [form processForLLVM]; } return value; } @end @implementation MLKSimpleLoopForm (MLKLLVMCompilation) -(Value *) processForLLVM { NSEnumerator *e = [_bodyForms objectEnumerator]; MLKForm *form; Function *function = builder.GetInsertBlock()->getParent(); BasicBlock *loopBlock = BasicBlock::Create ("loop", function); BasicBlock *joinBlock = BasicBlock::Create ("after_loop"); builder.CreateBr (loopBlock); builder.SetInsertPoint (loopBlock); while ((form = [e nextObject])) { [form processForLLVM]; } builder.CreateBr (loopBlock); builder.SetInsertPoint (joinBlock); function->getBasicBlockList().push_back (joinBlock); builder.CreateUnreachable (); return NULL; } @end @implementation MLKSymbolForm (MLKLLVMCompilation) -(Value *) processForLLVM { Value *value; if ([_context variableHeapAllocationForSymbol:_form]) { Value *binding = builder.CreateLoad ([_context bindingValueForSymbol:_form]); value = [_compiler insertMethodCall:@"value" onObject:binding]; } else { value = builder.CreateLoad ([_context valueValueForSymbol:_form], [MLKPrintToString(_form) UTF8String]); } return value; } @end @implementation MLKFunctionCallForm (MLKLLVMCompilation) -(Value *) processForLLVM { static MLKPackage *sys = [MLKPackage findPackage:@"TOILET-SYSTEM"]; BOOL special_dispatch = NO; Value *functionCell; Value *functionPtr; Value *closureDataCell; Value *closureDataPtr; std::vector args; if (![_context symbolNamesFunction:_head]) { if (_head && [_head homePackage] == sys) { special_dispatch = YES; } else { NSLog (@"Compiler: Don't know function %@", MLKPrintToString(_head)); // XXX Issue a style warning. } } if (!special_dispatch) { functionCell = builder.Insert ([_context functionCellValueForSymbol:_head]); functionPtr = builder.CreateLoad (functionCell); closureDataCell = builder.Insert ([_context closureDataPointerValueForSymbol:_head]); closureDataPtr = builder.CreateLoad (closureDataCell); args.push_back (closureDataPtr); } else { std::vector argtypes (1, PointerTy); functionPtr = builder.CreateIntToPtr (ConstantInt::get(Type::Int64Ty, (uint64_t)MLKDispatchRootFunction, false), PointerType::get (FunctionType::get (PointerTy, argtypes, true), 0)); LRETAIN (_head); // FIXME: release sometime? On the other hand, // these symbols will probably never be // deallocated anyway. args.push_back (builder.CreateIntToPtr (ConstantInt::get(Type::Int64Ty, (uint64_t)_head, false), PointerTy)); } NSEnumerator *e = [_argumentForms objectEnumerator]; MLKForm *form; while ((form = [e nextObject])) { args.push_back ([form processForLLVM]); } //GlobalVariable *endmarker = module->getGlobalVariable ("MLKEndOfArgumentsMarker", false); //endmarker->setConstant (true); //GlobalVariable *endmarker = new GlobalVariable (PointerTy, true, GlobalValue::ExternalWeakLinkage); Value *endmarker = builder.CreateIntToPtr (ConstantInt::get(Type::Int64Ty, (uint64_t)MLKEndOfArgumentsMarker, false), PointerTy); args.push_back (endmarker); CallInst *call = builder.CreateCall (functionPtr, args.begin(), args.end(), [MLKPrintToString(_head) UTF8String]); return call; } @end @implementation MLKSimpleLambdaForm (MLKLLVMCompilation) -(Value *) processForLLVM { std::vector argtypes (1, PointerTy); FunctionType *ftype = FunctionType::get (PointerTy, argtypes, true); Function *function = Function::Create (ftype, Function::InternalLinkage, "", module); Function::arg_iterator args = function->arg_begin(); Value *closure_data_arg = args++; closure_data_arg->setName ("closure_data"); BasicBlock *outerBlock = builder.GetInsertBlock (); BasicBlock *initBlock = BasicBlock::Create ("init_function", function); BasicBlock *loopBlock = BasicBlock::Create ("load_args"); BasicBlock *loopInitBlock = BasicBlock::Create ("load_args_prelude"); BasicBlock *joinBlock = BasicBlock::Create ("function_body"); BasicBlock *lambdaListNewBlock = BasicBlock::Create ("lambda_list_new"); BasicBlock *lambdaListUpdateBlock = BasicBlock::Create ("lambda_list_update"); builder.SetInsertPoint (initBlock); Value *endmarker = builder.CreateIntToPtr (ConstantInt::get(Type::Int64Ty, (uint64_t)MLKEndOfArgumentsMarker, false), PointerType::get(Type::Int8Ty, 0)); Value *ap = builder.CreateAlloca (Type::Int8Ty, NULL, "ap"); builder.CreateCall (module->getOrInsertFunction ("llvm.va_start", Type::VoidTy, PointerTy, NULL), ap); Value *mlkcons = [_compiler insertClassLookup:@"MLKCons"]; // FIXME: Heap-allocate if appropriate. Value *lambdaList = builder.CreateAlloca (PointerTy, NULL, "lambda_list"); Value *lambdaListTail = builder.CreateAlloca (PointerTy, NULL, "lambda_list_tail"); builder.CreateStore (ConstantPointerNull::get (PointerTy), lambdaList); builder.CreateStore (ConstantPointerNull::get (PointerTy), lambdaListTail); builder.CreateBr (loopInitBlock); builder.SetInsertPoint (loopInitBlock); function->getBasicBlockList().push_back (loopInitBlock); Value *arg = builder.CreateVAArg (ap, PointerTy, "arg"); Value *cond = builder.CreateICmpEQ (arg, endmarker); builder.CreateCondBr (cond, joinBlock, loopBlock); builder.SetInsertPoint (loopBlock); function->getBasicBlockList().push_back (loopBlock); builder.CreateCondBr (builder.CreateICmpEQ (builder.CreateLoad (lambdaList), ConstantPointerNull::get (PointerTy)), lambdaListNewBlock, lambdaListUpdateBlock); builder.SetInsertPoint (lambdaListNewBlock); function->getBasicBlockList().push_back (lambdaListNewBlock); std::vector argv (1, arg); argv.push_back (ConstantPointerNull::get (PointerTy)); Value *newLambdaList = [_compiler insertMethodCall:@"cons:with:" onObject:mlkcons withArgumentVector:&argv]; builder.CreateStore (newLambdaList, lambdaList); builder.CreateStore (newLambdaList, lambdaListTail); builder.CreateBr (loopInitBlock); builder.SetInsertPoint (lambdaListUpdateBlock); function->getBasicBlockList().push_back (lambdaListUpdateBlock); Value *newCons = [_compiler insertMethodCall:@"cons:with:" onObject:mlkcons withArgumentVector:&argv]; std::vector setcdr_argv (1, newCons); [_compiler insertVoidMethodCall:@"setCdr:" onObject:builder.CreateLoad(lambdaListTail) withArgumentVector:&setcdr_argv]; builder.CreateStore (newCons, lambdaListTail); builder.CreateBr (loopInitBlock); builder.SetInsertPoint (joinBlock); function->getBasicBlockList().push_back (joinBlock); builder.CreateCall (module->getOrInsertFunction ("llvm.va_end", Type::VoidTy, PointerTy, NULL), ap); NSEnumerator *e = [_bodyForms objectEnumerator]; MLKForm *form; Value *value = NULL; if ([_bodyForms count] == 0) { //NSLog (@"%LAMBDA: No body."); value = ConstantPointerNull::get (PointerTy); } while ((form = [e nextObject])) { //NSLog (@"%LAMBDA: Processing subform."); [form->_context setValueValue:lambdaList forSymbol:_lambdaListName]; value = [form processForLLVM]; } builder.CreateRet (value); function->dump(); //NSLog (@"Verify..."); verifyFunction (*function); //NSLog (@"Optimise..."); fpm->run (*function); //NSLog (@"Assemble..."); // Explicit assembly is needed in order to allow libffi to call // the function. execution_engine->getPointerToFunction (function); //NSLog (@"Done."); function->dump(); //NSLog (@"Function built."); builder.SetInsertPoint (outerBlock); Value *closure_data = ConstantPointerNull::get (PointerTy); argv[0] = function; argv[1] = closure_data; argv.push_back (builder.CreateIntToPtr (ConstantInt::get(Type::Int64Ty, 0, false), PointerTy)); Value *mlkcompiledclosure = [_compiler insertClassLookup:@"MLKCompiledClosure"]; Value *closure = [_compiler insertMethodCall:@"closureWithCode:data:length:" onObject:mlkcompiledclosure withArgumentVector:&argv]; //function->viewCFG(); return closure; } @end @implementation MLKLetForm (MLKLLVMCompilation) -(Value *) processForLLVM { NSEnumerator *e = [_variableBindingForms objectEnumerator]; Value *value = ConstantPointerNull::get (PointerTy); MLKForm *form; MLKVariableBindingForm *binding_form; while ((binding_form = [e nextObject])) { // FIXME: Handle heap allocation. Value *binding_value = [[binding_form valueForm] processForLLVM]; Value *binding_variable = builder.CreateAlloca (PointerTy, NULL, [(MLKPrintToString([binding_form name])) UTF8String]); builder.CreateStore (binding_value, binding_variable); [_bodyContext setValueValue:binding_variable forSymbol:[binding_form name]]; } e = [_bodyForms objectEnumerator]; while ((form = [e nextObject])) { value = [form processForLLVM]; } return value; } @end @implementation MLKQuoteForm (MLKLLVMCompilation) -(Value *) processForLLVM { // FIXME: When to release _quotedData? At the same time the code is // released, probably... LRETAIN (_quotedData); return builder.CreateIntToPtr (ConstantInt::get(Type::Int64Ty, (uint64_t)_quotedData, false), PointerTy); } @end @implementation MLKSelfEvaluatingForm (MLKLLVMCompilation) -(Value *) processForLLVM { // FIXME: When to release _form? At the same time the code is // released, probably... LRETAIN (_form); return builder.CreateIntToPtr (ConstantInt::get(Type::Int64Ty, (uint64_t)_form, false), PointerTy); } @end @implementation MLKIfForm (MLKLLVMCompilation) -(Value *) processForLLVM { Function *function = builder.GetInsertBlock()->getParent(); BasicBlock *thenBlock = BasicBlock::Create ("if_then", function); BasicBlock *elseBlock = BasicBlock::Create ("if_else"); BasicBlock *joinBlock = BasicBlock::Create ("if_join"); Value *thenValue, *elseValue; Value *test = builder.CreateICmpNE ([_conditionForm processForLLVM], ConstantPointerNull::get (PointerTy)); builder.CreateCondBr (test, thenBlock, elseBlock); builder.SetInsertPoint (thenBlock); thenValue = [_consequentForm processForLLVM]; builder.CreateBr (joinBlock); builder.SetInsertPoint (elseBlock); function->getBasicBlockList().push_back (elseBlock); elseValue = [_alternativeForm processForLLVM]; builder.CreateBr (joinBlock); builder.SetInsertPoint (joinBlock); function->getBasicBlockList().push_back (joinBlock); PHINode *value = builder.CreatePHI (PointerTy, "if_result"); value->addIncoming (thenValue, thenBlock); value->addIncoming (elseValue, elseBlock); return value; } @end @implementation MLKSetQForm (MLKLLVMCompilation) -(Value *) processForLLVM { NSEnumerator *var_e, *value_e; MLKForm *valueForm; Value *value = ConstantPointerNull::get (PointerTy); id variable; var_e = [_variables objectEnumerator]; value_e = [_valueForms objectEnumerator]; while ((valueForm = [value_e nextObject])) { variable = [var_e nextObject]; value = [valueForm processForLLVM]; if ([_context variableHeapAllocationForSymbol:variable]) { Value *binding = builder.CreateLoad ([_context bindingValueForSymbol:variable]); std::vector args (1, value); [_compiler insertVoidMethodCall:@"setValue:" onObject:binding withArgumentVector:&args]; } else { builder.CreateStore (value, [_context valueValueForSymbol:variable]); } } return value; } @end