1 //===----- Core.cpp - Core ORC APIs (MaterializationUnit, VSO, etc.) ------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "llvm/ExecutionEngine/Orc/Core.h" 11 #include "llvm/ExecutionEngine/Orc/OrcError.h" 12 13 #if LLVM_ENABLE_THREADS 14 #include <future> 15 #endif 16 17 namespace llvm { 18 namespace orc { 19 20 void MaterializationUnit::anchor() {} 21 void SymbolResolver::anchor() {} 22 23 AsynchronousSymbolQuery::AsynchronousSymbolQuery( 24 const SymbolNameSet &Symbols, SymbolsResolvedCallback NotifySymbolsResolved, 25 SymbolsReadyCallback NotifySymbolsReady) 26 : NotifySymbolsResolved(std::move(NotifySymbolsResolved)), 27 NotifySymbolsReady(std::move(NotifySymbolsReady)) { 28 assert(this->NotifySymbolsResolved && 29 "Symbols resolved callback must be set"); 30 assert(this->NotifySymbolsReady && "Symbols ready callback must be set"); 31 OutstandingResolutions = OutstandingFinalizations = Symbols.size(); 32 } 33 34 void AsynchronousSymbolQuery::setFailed(Error Err) { 35 OutstandingResolutions = OutstandingFinalizations = 0; 36 if (NotifySymbolsResolved) 37 NotifySymbolsResolved(std::move(Err)); 38 else 39 NotifySymbolsReady(std::move(Err)); 40 } 41 42 void AsynchronousSymbolQuery::setDefinition(SymbolStringPtr Name, 43 JITEvaluatedSymbol Sym) { 44 // If OutstandingResolutions is zero we must have errored out already. Just 45 // ignore this. 46 if (OutstandingResolutions == 0) 47 return; 48 49 assert(!Symbols.count(Name) && "Symbol has already been assigned an address"); 50 Symbols.insert(std::make_pair(std::move(Name), std::move(Sym))); 51 --OutstandingResolutions; 52 if (OutstandingResolutions == 0) { 53 NotifySymbolsResolved(std::move(Symbols)); 54 // Null out NotifySymbolsResolved to indicate that we've already called it. 55 NotifySymbolsResolved = {}; 56 } 57 } 58 59 void AsynchronousSymbolQuery::notifySymbolFinalized() { 60 // If OutstandingFinalizations is zero we must have errored out already. Just 61 // ignore this. 62 if (OutstandingFinalizations == 0) 63 return; 64 65 assert(OutstandingFinalizations > 0 && "All symbols already finalized"); 66 --OutstandingFinalizations; 67 if (OutstandingFinalizations == 0) 68 NotifySymbolsReady(Error::success()); 69 } 70 71 VSO::MaterializationInfo::MaterializationInfo( 72 size_t SymbolsRemaining, std::unique_ptr<MaterializationUnit> MU) 73 : SymbolsRemaining(SymbolsRemaining), MU(std::move(MU)) {} 74 75 VSO::SymbolTableEntry::SymbolTableEntry( 76 JITSymbolFlags Flags, MaterializationInfoIterator MaterializationInfoItr) 77 : Flags(JITSymbolFlags::FlagNames(Flags | JITSymbolFlags::NotMaterialized)), 78 MaterializationInfoItr(std::move(MaterializationInfoItr)) { 79 // FIXME: Assert flag sanity. 80 } 81 82 VSO::SymbolTableEntry::SymbolTableEntry(JITEvaluatedSymbol Sym) 83 : Flags(Sym.getFlags()), Address(Sym.getAddress()) { 84 // FIXME: Assert flag sanity. 85 } 86 87 VSO::SymbolTableEntry::SymbolTableEntry(SymbolTableEntry &&Other) 88 : Flags(Other.Flags), Address(0) { 89 if (Flags.isMaterialized()) 90 Address = Other.Address; 91 else 92 MaterializationInfoItr = std::move(Other.MaterializationInfoItr); 93 } 94 95 VSO::SymbolTableEntry::~SymbolTableEntry() { destroy(); } 96 97 VSO::SymbolTableEntry &VSO::SymbolTableEntry:: 98 operator=(JITEvaluatedSymbol Sym) { 99 destroy(); 100 Flags = Sym.getFlags(); 101 Address = Sym.getAddress(); 102 return *this; 103 } 104 105 void VSO::SymbolTableEntry::destroy() { 106 if (!Flags.isMaterialized()) 107 MaterializationInfoItr.~MaterializationInfoIterator(); 108 } 109 110 JITSymbolFlags VSO::SymbolTableEntry::getFlags() const { return Flags; } 111 112 void VSO::SymbolTableEntry::replaceWith( 113 VSO &V, SymbolStringPtr Name, JITSymbolFlags NewFlags, 114 MaterializationInfoIterator NewMaterializationInfoItr) { 115 bool ReplaceExistingLazyDefinition = !Flags.isMaterialized(); 116 Flags = NewFlags; 117 if (ReplaceExistingLazyDefinition) { 118 // If we are replacing an existing lazy definition with a stronger one, 119 // we need to notify the old lazy definition to discard its definition. 120 assert((*MaterializationInfoItr)->MU != nullptr && 121 (*MaterializationInfoItr)->Symbols.count(Name) == 0 && 122 (*MaterializationInfoItr)->PendingResolution.count(Name) == 0 && 123 (*MaterializationInfoItr)->PendingFinalization.count(Name) == 0 && 124 "Attempt to replace materializer during materialization"); 125 126 if (--(*MaterializationInfoItr)->SymbolsRemaining == 0) 127 V.MaterializationInfos.erase(MaterializationInfoItr); 128 } 129 MaterializationInfoItr = std::move(NewMaterializationInfoItr); 130 } 131 132 std::unique_ptr<MaterializationUnit> 133 VSO::SymbolTableEntry::query(SymbolStringPtr Name, 134 std::shared_ptr<AsynchronousSymbolQuery> Query) { 135 if (Flags.isMaterialized()) { 136 Query->setDefinition(std::move(Name), JITEvaluatedSymbol(Address, Flags)); 137 Query->notifySymbolFinalized(); 138 return nullptr; 139 } else { 140 if ((*MaterializationInfoItr)->MU) { 141 assert((*MaterializationInfoItr)->PendingResolution.count(Name) == 0 && 142 (*MaterializationInfoItr)->PendingFinalization.count(Name) == 0 && 143 "Materializer should have been activated on first query"); 144 (*MaterializationInfoItr) 145 ->PendingResolution[Name] 146 .push_back(std::move(Query)); 147 return std::move((*MaterializationInfoItr)->MU); 148 } else { 149 assert((*MaterializationInfoItr)->MU == nullptr && 150 "Materializer should have been activated on first query"); 151 auto SymValueItr = (*MaterializationInfoItr)->Symbols.find(Name); 152 if (SymValueItr == (*MaterializationInfoItr)->Symbols.end()) { 153 // Symbol has not been resolved yet. 154 (*MaterializationInfoItr) 155 ->PendingResolution[Name] 156 .push_back(std::move(Query)); 157 return nullptr; 158 } else { 159 // Symbol has already resolved, is just waiting on finalization. 160 Query->setDefinition(Name, SymValueItr->second); 161 (*MaterializationInfoItr) 162 ->PendingFinalization[Name] 163 .push_back(std::move(Query)); 164 return nullptr; 165 } 166 } 167 } 168 } 169 170 void VSO::SymbolTableEntry::resolve(VSO &V, SymbolStringPtr Name, 171 JITEvaluatedSymbol Sym) { 172 if (Flags.isMaterialized()) { 173 // FIXME: Should we assert flag state here (flags must match except for 174 // materialization state, overrides must be legal) or in the caller 175 // in VSO? 176 Flags = Sym.getFlags(); 177 Address = Sym.getAddress(); 178 } else { 179 assert((*MaterializationInfoItr)->MU == nullptr && 180 "Can not resolve a symbol that has not been materialized"); 181 assert((*MaterializationInfoItr)->Symbols.count(Name) == 0 && 182 "Symbol resolved more than once"); 183 184 // Add the symbol to the MaterializationInfo Symbols table. 185 (*MaterializationInfoItr)->Symbols[Name] = Sym; 186 187 // If there are any queries waiting on this symbol then notify them that it 188 // has been resolved, then move them to the PendingFinalization list. 189 auto I = (*MaterializationInfoItr)->PendingResolution.find(Name); 190 if (I != (*MaterializationInfoItr)->PendingResolution.end()) { 191 assert((*MaterializationInfoItr)->PendingFinalization.count(Name) == 0 && 192 "Queries already pending finalization on newly resolved symbol"); 193 auto &PendingFinalization = 194 (*MaterializationInfoItr)->PendingFinalization[Name]; 195 196 for (auto &Query : I->second) { 197 Query->setDefinition(Name, Sym); 198 PendingFinalization.push_back(Query); 199 } 200 201 // Clear the PendingResolution list for this symbol. 202 (*MaterializationInfoItr)->PendingResolution.erase(I); 203 } 204 } 205 } 206 207 void VSO::SymbolTableEntry::finalize(VSO &V, SymbolStringPtr Name) { 208 if (!Flags.isMaterialized()) { 209 auto SymI = (*MaterializationInfoItr)->Symbols.find(Name); 210 assert(SymI != (*MaterializationInfoItr)->Symbols.end() && 211 "Finalizing an unresolved symbol"); 212 auto Sym = SymI->second; 213 (*MaterializationInfoItr)->Symbols.erase(SymI); 214 auto I = (*MaterializationInfoItr)->PendingFinalization.find(Name); 215 if (I != (*MaterializationInfoItr)->PendingFinalization.end()) { 216 for (auto &Query : I->second) 217 Query->notifySymbolFinalized(); 218 (*MaterializationInfoItr)->PendingFinalization.erase(I); 219 } 220 221 if (--(*MaterializationInfoItr)->SymbolsRemaining == 0) 222 V.MaterializationInfos.erase(MaterializationInfoItr); 223 224 // Destruct the iterator and re-define this entry using the final symbol 225 // value. 226 destroy(); 227 Flags = Sym.getFlags(); 228 Address = Sym.getAddress(); 229 } 230 assert(Flags.isMaterialized() && "Trying to finalize not-emitted symbol"); 231 } 232 233 void VSO::SymbolTableEntry::discard(VSO &V, SymbolStringPtr Name) { 234 assert((*MaterializationInfoItr)->MU != nullptr && 235 "Can not override a symbol after it has been materialized"); 236 (*MaterializationInfoItr)->MU->discard(V, Name); 237 --(*MaterializationInfoItr)->SymbolsRemaining; 238 } 239 240 VSO::RelativeLinkageStrength VSO::compareLinkage(Optional<JITSymbolFlags> Old, 241 JITSymbolFlags New) { 242 if (Old == None) 243 return llvm::orc::VSO::NewDefinitionIsStronger; 244 245 if (Old->isStrong()) { 246 if (New.isStrong()) 247 return llvm::orc::VSO::DuplicateDefinition; 248 else 249 return llvm::orc::VSO::ExistingDefinitionIsStronger; 250 } else { 251 if (New.isStrong()) 252 return llvm::orc::VSO::NewDefinitionIsStronger; 253 else 254 return llvm::orc::VSO::ExistingDefinitionIsStronger; 255 } 256 } 257 258 VSO::RelativeLinkageStrength 259 VSO::compareLinkage(SymbolStringPtr Name, JITSymbolFlags NewFlags) const { 260 auto I = Symbols.find(Name); 261 return compareLinkage(I == Symbols.end() 262 ? None 263 : Optional<JITSymbolFlags>(I->second.getFlags()), 264 NewFlags); 265 } 266 267 Error VSO::define(SymbolMap NewSymbols) { 268 Error Err = Error::success(); 269 for (auto &KV : NewSymbols) { 270 auto I = Symbols.find(KV.first); 271 auto LinkageResult = compareLinkage( 272 I == Symbols.end() ? None 273 : Optional<JITSymbolFlags>(I->second.getFlags()), 274 KV.second.getFlags()); 275 276 // Silently discard weaker definitions. 277 if (LinkageResult == ExistingDefinitionIsStronger) 278 continue; 279 280 // Report duplicate definition errors. 281 if (LinkageResult == DuplicateDefinition) { 282 Err = joinErrors(std::move(Err), 283 make_error<orc::DuplicateDefinition>(*KV.first)); 284 continue; 285 } 286 287 if (I != Symbols.end()) { 288 // This is an override -- discard the overridden definition and overwrite. 289 I->second.discard(*this, KV.first); 290 I->second = std::move(KV.second); 291 } else 292 Symbols.insert(std::make_pair(KV.first, std::move(KV.second))); 293 } 294 return Err; 295 } 296 297 Error VSO::defineLazy(std::unique_ptr<MaterializationUnit> MU) { 298 299 auto NewSymbols = MU->getSymbols(); 300 301 auto MaterializationInfoItr = 302 MaterializationInfos 303 .insert(llvm::make_unique<MaterializationInfo>(NewSymbols.size(), 304 std::move(MU))) 305 .first; 306 307 Error Err = Error::success(); 308 for (auto &KV : NewSymbols) { 309 auto I = Symbols.find(KV.first); 310 311 auto LinkageResult = compareLinkage( 312 I == Symbols.end() ? None 313 : Optional<JITSymbolFlags>(I->second.getFlags()), 314 KV.second); 315 316 // Discard weaker definitions. 317 if (LinkageResult == ExistingDefinitionIsStronger) { 318 (*MaterializationInfoItr)->MU->discard(*this, KV.first); 319 assert((*MaterializationInfoItr)->SymbolsRemaining > 0 && 320 "Discarding non-existant symbols?"); 321 --(*MaterializationInfoItr)->SymbolsRemaining; 322 continue; 323 } 324 325 // Report duplicate definition errors. 326 if (LinkageResult == DuplicateDefinition) { 327 Err = joinErrors(std::move(Err), 328 make_error<orc::DuplicateDefinition>(*KV.first)); 329 // Duplicate definitions are discarded, so remove the duplicates from 330 // materializer. 331 assert((*MaterializationInfoItr)->SymbolsRemaining > 0 && 332 "Discarding non-existant symbols?"); 333 --(*MaterializationInfoItr)->SymbolsRemaining; 334 continue; 335 } 336 337 if (I != Symbols.end()) 338 I->second.replaceWith(*this, KV.first, KV.second, MaterializationInfoItr); 339 else 340 Symbols.emplace(std::make_pair( 341 KV.first, SymbolTableEntry(KV.second, MaterializationInfoItr))); 342 } 343 344 // If we ended up overriding all definitions in this materializer then delete 345 // it. 346 if ((*MaterializationInfoItr)->SymbolsRemaining == 0) 347 MaterializationInfos.erase(MaterializationInfoItr); 348 349 return Err; 350 } 351 352 void VSO::resolve(SymbolMap SymbolValues) { 353 for (auto &KV : SymbolValues) { 354 auto I = Symbols.find(KV.first); 355 assert(I != Symbols.end() && "Resolving symbol not present in this dylib"); 356 I->second.resolve(*this, KV.first, std::move(KV.second)); 357 } 358 } 359 360 void VSO::finalize(SymbolNameSet SymbolsToFinalize) { 361 for (auto &S : SymbolsToFinalize) { 362 auto I = Symbols.find(S); 363 assert(I != Symbols.end() && "Finalizing symbol not present in this dylib"); 364 I->second.finalize(*this, S); 365 } 366 } 367 368 SymbolNameSet VSO::lookupFlags(SymbolFlagsMap &Flags, SymbolNameSet Names) { 369 370 for (SymbolNameSet::iterator I = Names.begin(), E = Names.end(); I != E;) { 371 auto Tmp = I++; 372 auto SymI = Symbols.find(*Tmp); 373 374 // If the symbol isn't in this dylib then just continue. 375 if (SymI == Symbols.end()) 376 continue; 377 378 Names.erase(Tmp); 379 380 Flags[SymI->first] = 381 JITSymbolFlags::stripTransientFlags(SymI->second.getFlags()); 382 } 383 384 return Names; 385 } 386 387 VSO::LookupResult VSO::lookup(std::shared_ptr<AsynchronousSymbolQuery> Query, 388 SymbolNameSet Names) { 389 MaterializationUnitList MaterializationUnits; 390 391 for (SymbolNameSet::iterator I = Names.begin(), E = Names.end(); I != E;) { 392 auto Tmp = I++; 393 auto SymI = Symbols.find(*Tmp); 394 395 // If the symbol isn't in this dylib then just continue. 396 if (SymI == Symbols.end()) 397 continue; 398 399 // The symbol is in the dylib. Erase it from Names and proceed. 400 Names.erase(Tmp); 401 402 // Forward the query to the given SymbolTableEntry, and if it return a 403 // layer to perform materialization with, add that to the 404 // MaterializationWork map. 405 if (auto MU = SymI->second.query(SymI->first, Query)) 406 MaterializationUnits.push_back(std::move(MU)); 407 } 408 409 return {std::move(MaterializationUnits), std::move(Names)}; 410 } 411 412 Expected<SymbolMap> lookup(const std::vector<VSO *> &VSOs, SymbolNameSet Names, 413 MaterializationDispatcher DispatchMaterialization) { 414 #if LLVM_ENABLE_THREADS 415 // In the threaded case we use promises to return the results. 416 std::promise<SymbolMap> PromisedResult; 417 std::mutex ErrMutex; 418 Error ResolutionError = Error::success(); 419 std::promise<void> PromisedReady; 420 Error ReadyError = Error::success(); 421 auto OnResolve = [&](Expected<SymbolMap> Result) { 422 if (Result) 423 PromisedResult.set_value(std::move(*Result)); 424 else { 425 { 426 ErrorAsOutParameter _(&ResolutionError); 427 std::lock_guard<std::mutex> Lock(ErrMutex); 428 ResolutionError = Result.takeError(); 429 } 430 PromisedResult.set_value(SymbolMap()); 431 } 432 }; 433 auto OnReady = [&](Error Err) { 434 if (Err) { 435 ErrorAsOutParameter _(&ReadyError); 436 std::lock_guard<std::mutex> Lock(ErrMutex); 437 ReadyError = std::move(Err); 438 } 439 PromisedReady.set_value(); 440 }; 441 #else 442 SymbolMap Result; 443 Error ResolutionError = Error::success(); 444 Error ReadyError = Error::success(); 445 446 auto OnResolve = [&](Expected<SymbolMap> R) { 447 ErrorAsOutParameter _(&ResolutionError); 448 if (R) 449 Result = std::move(*R); 450 else 451 ResolutionError = R.takeError(); 452 }; 453 auto OnReady = [&](Error Err) { 454 ErrorAsOutParameter _(&ReadyError); 455 if (Err) 456 ReadyError = std::move(Err); 457 }; 458 #endif 459 460 auto Query = std::make_shared<AsynchronousSymbolQuery>( 461 Names, std::move(OnResolve), std::move(OnReady)); 462 SymbolNameSet UnresolvedSymbols(std::move(Names)); 463 464 for (auto *V : VSOs) { 465 466 if (UnresolvedSymbols.empty()) 467 break; 468 469 assert(V && "VSO pointers in VSOs list should be non-null"); 470 auto LR = V->lookup(Query, UnresolvedSymbols); 471 UnresolvedSymbols = std::move(LR.UnresolvedSymbols); 472 473 for (auto &MU : LR.MaterializationUnits) 474 DispatchMaterialization(*V, std::move(MU)); 475 } 476 477 #if LLVM_ENABLE_THREADS 478 auto ResultFuture = PromisedResult.get_future(); 479 auto Result = ResultFuture.get(); 480 481 { 482 std::lock_guard<std::mutex> Lock(ErrMutex); 483 if (ResolutionError) { 484 // ReadyError will never be assigned. Consume the success value. 485 cantFail(std::move(ReadyError)); 486 return std::move(ResolutionError); 487 } 488 } 489 490 auto ReadyFuture = PromisedReady.get_future(); 491 ReadyFuture.get(); 492 493 { 494 std::lock_guard<std::mutex> Lock(ErrMutex); 495 if (ReadyError) 496 return std::move(ReadyError); 497 } 498 499 return std::move(Result); 500 501 #else 502 if (ResolutionError) { 503 // ReadyError will never be assigned. Consume the success value. 504 cantFail(std::move(ReadyError)); 505 return std::move(ResolutionError); 506 } 507 508 if (ReadyError) 509 return std::move(ReadyError); 510 511 return Result; 512 #endif 513 } 514 515 /// @brief Look up a symbol by searching a list of VSOs. 516 Expected<JITEvaluatedSymbol> 517 lookup(const std::vector<VSO *> VSOs, SymbolStringPtr Name, 518 MaterializationDispatcher DispatchMaterialization) { 519 SymbolNameSet Names({Name}); 520 if (auto ResultMap = 521 lookup(VSOs, std::move(Names), std::move(DispatchMaterialization))) { 522 assert(ResultMap->size() == 1 && "Unexpected number of results"); 523 assert(ResultMap->count(Name) && "Missing result for symbol"); 524 return ResultMap->begin()->second; 525 } else 526 return ResultMap.takeError(); 527 } 528 529 void ExecutionSession::logErrorsToStdErr(Error Err) { 530 logAllUnhandledErrors(std::move(Err), errs(), "JIT session error: "); 531 } 532 533 } // End namespace orc. 534 } // End namespace llvm. 535