From c0981da47d5696fe36474fcf86b4ce03ae3ff818 Mon Sep 17 00:00:00 2001 From: Dimitry Andric Date: Fri, 19 Nov 2021 21:06:13 +0100 Subject: Vendor import of llvm-project main llvmorg-14-init-10186-gff7f2cfa959b. --- llvm/include/llvm-c/Comdat.h | 11 + llvm/include/llvm-c/Core.h | 16 +- llvm/include/llvm-c/DebugInfo.h | 82 +- llvm/include/llvm-c/DisassemblerTypes.h | 10 + llvm/include/llvm-c/Error.h | 11 + llvm/include/llvm-c/ErrorHandling.h | 10 + llvm/include/llvm-c/IRReader.h | 11 + llvm/include/llvm-c/LLJIT.h | 11 + llvm/include/llvm-c/Linker.h | 11 + llvm/include/llvm-c/Orc.h | 54 + llvm/include/llvm-c/OrcEE.h | 11 + llvm/include/llvm-c/Support.h | 10 + llvm/include/llvm-c/TargetMachine.h | 10 + llvm/include/llvm-c/Transforms/PassBuilder.h | 13 +- llvm/include/llvm-c/lto.h | 12 +- llvm/include/llvm/ADT/APFloat.h | 4 +- llvm/include/llvm/ADT/APInt.h | 935 ++++++----- llvm/include/llvm/ADT/APSInt.h | 10 +- llvm/include/llvm/ADT/ArrayRef.h | 4 +- llvm/include/llvm/ADT/BitVector.h | 24 +- llvm/include/llvm/ADT/CombinationGenerator.h | 148 ++ llvm/include/llvm/ADT/DenseMapInfo.h | 16 +- llvm/include/llvm/ADT/EquivalenceClasses.h | 33 +- llvm/include/llvm/ADT/FunctionExtras.h | 16 +- llvm/include/llvm/ADT/Hashing.h | 8 + llvm/include/llvm/ADT/ImmutableList.h | 3 +- llvm/include/llvm/ADT/IntervalMap.h | 2 +- llvm/include/llvm/ADT/MapVector.h | 1 + llvm/include/llvm/ADT/PointerIntPair.h | 4 +- llvm/include/llvm/ADT/PointerUnion.h | 31 +- llvm/include/llvm/ADT/STLExtras.h | 171 +- llvm/include/llvm/ADT/Sequence.h | 164 +- llvm/include/llvm/ADT/SetOperations.h | 9 - llvm/include/llvm/ADT/SmallBitVector.h | 51 +- llvm/include/llvm/ADT/SmallVector.h | 15 +- llvm/include/llvm/ADT/StringExtras.h | 137 +- llvm/include/llvm/ADT/StringMap.h | 27 +- llvm/include/llvm/ADT/StringRef.h | 3 +- llvm/include/llvm/ADT/Triple.h | 158 +- llvm/include/llvm/ADT/TypeSwitch.h | 7 +- llvm/include/llvm/ADT/iterator.h | 49 +- llvm/include/llvm/Analysis/AliasAnalysis.h | 69 +- llvm/include/llvm/Analysis/AssumeBundleQueries.h | 9 +- llvm/include/llvm/Analysis/AssumptionCache.h | 10 +- llvm/include/llvm/Analysis/BasicAliasAnalysis.h | 82 +- llvm/include/llvm/Analysis/CGSCCPassManager.h | 101 +- llvm/include/llvm/Analysis/CaptureTracking.h | 25 +- llvm/include/llvm/Analysis/ConstantFolding.h | 30 +- llvm/include/llvm/Analysis/CostModel.h | 26 + llvm/include/llvm/Analysis/Delinearization.h | 105 ++ llvm/include/llvm/Analysis/HeatUtils.h | 7 +- .../include/llvm/Analysis/IRSimilarityIdentifier.h | 250 ++- llvm/include/llvm/Analysis/IVDescriptors.h | 88 +- llvm/include/llvm/Analysis/IVUsers.h | 3 - llvm/include/llvm/Analysis/InlineAdvisor.h | 38 +- llvm/include/llvm/Analysis/InlineCost.h | 3 + llvm/include/llvm/Analysis/InlineOrder.h | 172 ++ llvm/include/llvm/Analysis/InstructionSimplify.h | 2 +- llvm/include/llvm/Analysis/LazyCallGraph.h | 65 +- llvm/include/llvm/Analysis/LoopAccessAnalysis.h | 26 +- llvm/include/llvm/Analysis/LoopAnalysisManager.h | 1 + llvm/include/llvm/Analysis/LoopInfo.h | 17 +- llvm/include/llvm/Analysis/LoopInfoImpl.h | 5 +- llvm/include/llvm/Analysis/LoopNestAnalysis.h | 22 +- llvm/include/llvm/Analysis/MLInlineAdvisor.h | 2 + llvm/include/llvm/Analysis/MemorySSA.h | 30 +- llvm/include/llvm/Analysis/ObjCARCAnalysisUtils.h | 28 +- llvm/include/llvm/Analysis/ObjCARCUtil.h | 45 +- llvm/include/llvm/Analysis/ProfileSummaryInfo.h | 12 +- llvm/include/llvm/Analysis/ReplayInlineAdvisor.h | 53 +- llvm/include/llvm/Analysis/ScalarEvolution.h | 282 ++-- llvm/include/llvm/Analysis/StackLifetime.h | 2 + llvm/include/llvm/Analysis/StackSafetyAnalysis.h | 8 + llvm/include/llvm/Analysis/TargetLibraryInfo.h | 6 +- llvm/include/llvm/Analysis/TargetTransformInfo.h | 83 +- .../llvm/Analysis/TargetTransformInfoImpl.h | 131 +- llvm/include/llvm/Analysis/TypeMetadataUtils.h | 28 +- llvm/include/llvm/Analysis/Utils/TFUtils.h | 7 +- llvm/include/llvm/Analysis/ValueTracking.h | 36 +- llvm/include/llvm/Analysis/VectorUtils.h | 12 +- llvm/include/llvm/AsmParser/LLLexer.h | 4 +- llvm/include/llvm/AsmParser/LLParser.h | 27 +- llvm/include/llvm/AsmParser/LLToken.h | 8 +- llvm/include/llvm/BinaryFormat/Dwarf.def | 3 + llvm/include/llvm/BinaryFormat/DynamicTags.def | 12 + llvm/include/llvm/BinaryFormat/ELF.h | 23 + llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def | 4 - llvm/include/llvm/BinaryFormat/MachO.def | 2 + llvm/include/llvm/BinaryFormat/Wasm.h | 41 +- llvm/include/llvm/BinaryFormat/WasmTraits.h | 18 +- llvm/include/llvm/BinaryFormat/XCOFF.h | 16 + llvm/include/llvm/Bitcode/BitcodeAnalyzer.h | 2 + llvm/include/llvm/Bitcode/BitcodeCommon.h | 8 +- llvm/include/llvm/Bitcode/LLVMBitCodes.h | 1 + llvm/include/llvm/CodeGen/Analysis.h | 5 +- llvm/include/llvm/CodeGen/AsmPrinter.h | 7 +- llvm/include/llvm/CodeGen/BasicTTIImpl.h | 264 ++-- llvm/include/llvm/CodeGen/CodeGenCommonISel.h | 219 +++ llvm/include/llvm/CodeGen/CommandFlags.h | 7 +- llvm/include/llvm/CodeGen/FunctionLoweringInfo.h | 1 - .../include/llvm/CodeGen/GlobalISel/CallLowering.h | 14 +- .../llvm/CodeGen/GlobalISel/CombinerHelper.h | 131 +- .../llvm/CodeGen/GlobalISel/GenericMachineInstrs.h | 37 +- .../include/llvm/CodeGen/GlobalISel/IRTranslator.h | 38 +- .../llvm/CodeGen/GlobalISel/LegacyLegalizerInfo.h | 2 +- .../GlobalISel/LegalizationArtifactCombiner.h | 184 ++- .../llvm/CodeGen/GlobalISel/LegalizerHelper.h | 17 + .../llvm/CodeGen/GlobalISel/LegalizerInfo.h | 29 +- .../include/llvm/CodeGen/GlobalISel/LoadStoreOpt.h | 165 ++ .../llvm/CodeGen/GlobalISel/MIPatternMatch.h | 49 +- .../llvm/CodeGen/GlobalISel/MachineIRBuilder.h | 8 + llvm/include/llvm/CodeGen/GlobalISel/Utils.h | 100 +- llvm/include/llvm/CodeGen/ISDOpcodes.h | 11 + llvm/include/llvm/CodeGen/IndirectThunks.h | 2 +- .../llvm/CodeGen/LinkAllAsmWriterComponents.h | 3 + .../llvm/CodeGen/LinkAllCodegenComponents.h | 3 + llvm/include/llvm/CodeGen/LiveInterval.h | 10 +- llvm/include/llvm/CodeGen/LiveIntervalUnion.h | 29 +- llvm/include/llvm/CodeGen/LiveVariables.h | 6 + llvm/include/llvm/CodeGen/LowLevelType.h | 3 +- llvm/include/llvm/CodeGen/MIRFSDiscriminator.h | 4 + llvm/include/llvm/CodeGen/MIRFormatter.h | 7 +- llvm/include/llvm/CodeGen/MIRSampleProfile.h | 76 + llvm/include/llvm/CodeGen/MIRYamlMapping.h | 2 + llvm/include/llvm/CodeGen/MachineCombinerPattern.h | 13 +- llvm/include/llvm/CodeGen/MachineDominators.h | 16 +- llvm/include/llvm/CodeGen/MachineFrameInfo.h | 2 + llvm/include/llvm/CodeGen/MachineFunction.h | 16 +- llvm/include/llvm/CodeGen/MachineInstr.h | 14 +- llvm/include/llvm/CodeGen/MachineMemOperand.h | 12 +- .../CodeGen/MachineOptimizationRemarkEmitter.h | 6 + llvm/include/llvm/CodeGen/MachineRegisterInfo.h | 41 +- llvm/include/llvm/CodeGen/MacroFusion.h | 14 + llvm/include/llvm/CodeGen/Passes.h | 12 + llvm/include/llvm/CodeGen/RegAllocCommon.h | 7 +- llvm/include/llvm/CodeGen/RegisterScavenging.h | 3 - llvm/include/llvm/CodeGen/SelectionDAG.h | 91 +- .../llvm/CodeGen/SelectionDAGAddressAnalysis.h | 1 + llvm/include/llvm/CodeGen/SelectionDAGNodes.h | 238 ++- llvm/include/llvm/CodeGen/SwitchLoweringUtils.h | 8 +- llvm/include/llvm/CodeGen/TargetCallingConv.h | 8 +- llvm/include/llvm/CodeGen/TargetInstrInfo.h | 33 +- llvm/include/llvm/CodeGen/TargetLowering.h | 84 +- llvm/include/llvm/CodeGen/TargetPassConfig.h | 14 +- llvm/include/llvm/CodeGen/TargetRegisterInfo.h | 6 +- llvm/include/llvm/CodeGen/TargetSchedule.h | 1 - llvm/include/llvm/CodeGen/ValueTypes.td | 3 +- llvm/include/llvm/DWARFLinker/DWARFLinker.h | 24 +- llvm/include/llvm/DebugInfo/CodeView/CVRecord.h | 1 - .../llvm/DebugInfo/CodeView/CodeViewRegisters.def | 2 +- llvm/include/llvm/DebugInfo/CodeView/TypeIndex.h | 1 + .../DebugInfo/DWARF/DWARFAbbreviationDeclaration.h | 21 + .../llvm/DebugInfo/DWARF/DWARFAddressRange.h | 6 +- llvm/include/llvm/DebugInfo/DWARF/DWARFContext.h | 28 +- .../llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h | 32 +- llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h | 4 - .../llvm/DebugInfo/DWARF/DWARFDebugRangeList.h | 7 +- llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h | 14 +- .../include/llvm/DebugInfo/DWARF/DWARFExpression.h | 40 +- llvm/include/llvm/DebugInfo/DWARF/DWARFFormValue.h | 17 +- llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h | 30 +- llvm/include/llvm/DebugInfo/GSYM/StringTable.h | 1 - llvm/include/llvm/DebugInfo/MSF/MSFCommon.h | 3 + .../include/llvm/DebugInfo/MSF/MappedBlockStream.h | 20 +- .../llvm/DebugInfo/PDB/Native/DbiModuleList.h | 4 +- llvm/include/llvm/DebugInfo/PDB/Native/HashTable.h | 5 +- .../llvm/DebugInfo/PDB/Native/NamedStreamMap.h | 1 - .../llvm/DebugInfo/PDB/Native/NativeLineNumber.h | 1 - .../DebugInfo/PDB/Native/NativeTypeFunctionSig.h | 1 - .../llvm/DebugInfo/PDB/Native/NativeTypeVTShape.h | 1 - .../llvm/DebugInfo/PDB/Native/PDBFileBuilder.h | 1 - llvm/include/llvm/Demangle/Demangle.h | 14 +- llvm/include/llvm/Demangle/ItaniumDemangle.h | 1315 ++++++++-------- .../include/llvm/Demangle/MicrosoftDemangleNodes.h | 93 +- llvm/include/llvm/Demangle/Utility.h | 51 +- .../include/llvm/ExecutionEngine/ExecutionEngine.h | 1 - .../llvm/ExecutionEngine/JITLink/ELF_aarch64.h | 39 + .../llvm/ExecutionEngine/JITLink/ELF_riscv.h | 2 +- .../llvm/ExecutionEngine/JITLink/ELF_x86_64.h | 20 +- .../include/llvm/ExecutionEngine/JITLink/JITLink.h | 98 +- .../ExecutionEngine/JITLink/JITLinkMemoryManager.h | 420 ++++- .../llvm/ExecutionEngine/JITLink/MachO_arm64.h | 2 + .../llvm/ExecutionEngine/JITLink/MemoryFlags.h | 225 +++ .../llvm/ExecutionEngine/JITLink/TableManager.h | 63 + .../include/llvm/ExecutionEngine/JITLink/aarch64.h | 38 + llvm/include/llvm/ExecutionEngine/JITLink/riscv.h | 14 +- llvm/include/llvm/ExecutionEngine/JITLink/x86_64.h | 275 +++- llvm/include/llvm/ExecutionEngine/MCJIT.h | 3 + llvm/include/llvm/ExecutionEngine/Orc/Core.h | 100 +- .../ExecutionEngine/Orc/DebuggerSupportPlugin.h | 64 + .../llvm/ExecutionEngine/Orc/ELFNixPlatform.h | 330 ++++ .../ExecutionEngine/Orc/EPCDebugObjectRegistrar.h | 9 +- .../llvm/ExecutionEngine/Orc/EPCEHFrameRegistrar.h | 9 +- .../ExecutionEngine/Orc/EPCGenericDylibManager.h | 67 + .../Orc/EPCGenericJITLinkMemoryManager.h | 97 ++ .../ExecutionEngine/Orc/EPCGenericMemoryAccess.h | 85 + .../Orc/EPCGenericRTDyldMemoryManager.h | 133 ++ .../llvm/ExecutionEngine/Orc/EPCIndirectionUtils.h | 6 +- .../ExecutionEngine/Orc/ExecutorProcessControl.h | 272 +++- .../llvm/ExecutionEngine/Orc/IndirectionUtils.h | 34 + .../llvm/ExecutionEngine/Orc/LLVMSPSSerializers.h | 69 - .../ExecutionEngine/Orc/LookupAndRecordAddrs.h | 70 + .../llvm/ExecutionEngine/Orc/MachOPlatform.h | 88 +- .../llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h | 6 +- .../Orc/OrcRPCExecutorProcessControl.h | 436 ----- .../ExecutionEngine/Orc/OrcRemoteTargetClient.h | 925 ----------- .../ExecutionEngine/Orc/OrcRemoteTargetRPCAPI.h | 386 ----- .../ExecutionEngine/Orc/OrcRemoteTargetServer.h | 464 ------ .../ExecutionEngine/Orc/Shared/ExecutorAddress.h | 138 +- .../ExecutionEngine/Orc/Shared/FDRawByteChannel.h | 79 - .../llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h | 68 + .../llvm/ExecutionEngine/Orc/Shared/RPCUtils.h | 1659 -------------------- .../ExecutionEngine/Orc/Shared/RawByteChannel.h | 183 --- .../ExecutionEngine/Orc/Shared/Serialization.h | 769 --------- .../Orc/Shared/SimplePackedSerialization.h | 124 +- .../Orc/Shared/SimpleRemoteEPCUtils.h | 235 +++ .../Orc/Shared/TargetProcessControlTypes.h | 286 +++- .../Orc/Shared/WrapperFunctionUtils.h | 124 +- .../llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h | 140 ++ .../Orc/TargetProcess/ExecutorBootstrapService.h | 36 + .../Orc/TargetProcess/JITLoaderGDB.h | 2 +- .../Orc/TargetProcess/OrcRPCTPCServer.h | 660 -------- .../Orc/TargetProcess/RegisterEHFrames.h | 20 +- .../Orc/TargetProcess/SimpleExecutorDylibManager.h | 64 + .../TargetProcess/SimpleExecutorMemoryManager.h | 70 + .../Orc/TargetProcess/SimpleRemoteEPCServer.h | 182 +++ .../llvm/ExecutionEngine/Orc/TaskDispatch.h | 131 ++ .../llvm/ExecutionEngine/OrcMCJITReplacement.h | 37 - .../llvm/ExecutionEngine/OrcV1Deprecation.h | 22 - llvm/include/llvm/ExecutionEngine/RuntimeDyld.h | 14 + llvm/include/llvm/Frontend/OpenMP/OMP.td | 69 +- llvm/include/llvm/Frontend/OpenMP/OMPConstants.h | 8 + llvm/include/llvm/Frontend/OpenMP/OMPGridValues.h | 117 +- llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h | 430 ++++- llvm/include/llvm/Frontend/OpenMP/OMPKinds.def | 63 +- llvm/include/llvm/IR/AbstractCallSite.h | 2 +- llvm/include/llvm/IR/Argument.h | 2 +- llvm/include/llvm/IR/Assumptions.h | 22 +- llvm/include/llvm/IR/Attributes.h | 371 +++-- llvm/include/llvm/IR/Attributes.td | 3 + llvm/include/llvm/IR/BasicBlock.h | 12 +- llvm/include/llvm/IR/Constant.h | 6 + llvm/include/llvm/IR/ConstantRange.h | 40 + llvm/include/llvm/IR/Constants.h | 15 +- llvm/include/llvm/IR/DIBuilder.h | 80 +- llvm/include/llvm/IR/DataLayout.h | 23 +- llvm/include/llvm/IR/DebugInfo.h | 2 - llvm/include/llvm/IR/DebugInfoMetadata.h | 437 +++--- llvm/include/llvm/IR/DerivedTypes.h | 7 +- llvm/include/llvm/IR/DiagnosticInfo.h | 36 +- llvm/include/llvm/IR/DiagnosticPrinter.h | 2 +- llvm/include/llvm/IR/Dominators.h | 6 + llvm/include/llvm/IR/FPEnv.h | 14 +- llvm/include/llvm/IR/Function.h | 252 ++- llvm/include/llvm/IR/GCStrategy.h | 3 + llvm/include/llvm/IR/GlobalAlias.h | 33 +- llvm/include/llvm/IR/GlobalIFunc.h | 44 +- llvm/include/llvm/IR/GlobalIndirectSymbol.h | 93 -- llvm/include/llvm/IR/GlobalObject.h | 7 +- llvm/include/llvm/IR/GlobalValue.h | 14 +- llvm/include/llvm/IR/IRBuilder.h | 32 +- llvm/include/llvm/IR/InstrTypes.h | 292 ++-- llvm/include/llvm/IR/Instruction.h | 18 +- llvm/include/llvm/IR/Instructions.h | 105 +- llvm/include/llvm/IR/IntrinsicInst.h | 22 + llvm/include/llvm/IR/Intrinsics.h | 3 +- llvm/include/llvm/IR/Intrinsics.td | 158 +- llvm/include/llvm/IR/IntrinsicsAArch64.td | 85 +- llvm/include/llvm/IR/IntrinsicsAMDGPU.td | 52 +- llvm/include/llvm/IR/IntrinsicsBPF.td | 3 + llvm/include/llvm/IR/IntrinsicsNVVM.td | 778 ++++----- llvm/include/llvm/IR/IntrinsicsPowerPC.td | 60 +- llvm/include/llvm/IR/IntrinsicsRISCV.td | 177 ++- llvm/include/llvm/IR/IntrinsicsSystemZ.td | 8 +- llvm/include/llvm/IR/IntrinsicsWebAssembly.td | 70 +- llvm/include/llvm/IR/IntrinsicsX86.td | 762 ++++++++- llvm/include/llvm/IR/LLVMContext.h | 4 + llvm/include/llvm/IR/MatrixBuilder.h | 32 +- llvm/include/llvm/IR/Metadata.h | 35 + llvm/include/llvm/IR/Module.h | 9 +- llvm/include/llvm/IR/ModuleSummaryIndex.h | 50 + llvm/include/llvm/IR/Operator.h | 7 + llvm/include/llvm/IR/OptBisect.h | 26 +- llvm/include/llvm/IR/PassManager.h | 81 +- llvm/include/llvm/IR/PassManagerInternal.h | 9 + llvm/include/llvm/IR/PatternMatch.h | 131 +- llvm/include/llvm/IR/ProfileSummary.h | 38 +- llvm/include/llvm/IR/PseudoProbe.h | 4 - llvm/include/llvm/IR/ReplaceConstant.h | 4 - llvm/include/llvm/IR/RuntimeLibcalls.def | 4 + llvm/include/llvm/IR/Type.h | 38 +- llvm/include/llvm/IR/VPIntrinsics.def | 141 +- llvm/include/llvm/IR/Value.h | 35 +- llvm/include/llvm/InitializePasses.h | 5 +- llvm/include/llvm/InterfaceStub/IFSHandler.h | 3 + llvm/include/llvm/LTO/Caching.h | 38 - llvm/include/llvm/LTO/Config.h | 3 + llvm/include/llvm/LTO/LTO.h | 47 +- llvm/include/llvm/LTO/SummaryBasedOptimizations.h | 2 + llvm/include/llvm/LTO/legacy/LTOCodeGenerator.h | 2 +- llvm/include/llvm/LTO/legacy/LTOModule.h | 4 + llvm/include/llvm/LinkAllIR.h | 3 + llvm/include/llvm/LinkAllPasses.h | 3 + llvm/include/llvm/MC/MCAsmBackend.h | 10 +- llvm/include/llvm/MC/MCAsmInfoGOFF.h | 29 + llvm/include/llvm/MC/MCContext.h | 2 +- llvm/include/llvm/MC/MCDwarf.h | 38 +- llvm/include/llvm/MC/MCELFObjectWriter.h | 2 + llvm/include/llvm/MC/MCELFStreamer.h | 2 +- llvm/include/llvm/MC/MCExpr.h | 2 + llvm/include/llvm/MC/MCFragment.h | 31 +- llvm/include/llvm/MC/MCInstrAnalysis.h | 11 +- llvm/include/llvm/MC/MCInstrDesc.h | 4 +- llvm/include/llvm/MC/MCObjectFileInfo.h | 4 + llvm/include/llvm/MC/MCObjectStreamer.h | 6 +- llvm/include/llvm/MC/MCPseudoProbe.h | 292 +++- llvm/include/llvm/MC/MCRegister.h | 1 + llvm/include/llvm/MC/MCSchedule.h | 1 - llvm/include/llvm/MC/MCStreamer.h | 10 +- llvm/include/llvm/MC/MCSymbolWasm.h | 12 +- llvm/include/llvm/MC/MCWasmStreamer.h | 5 + llvm/include/llvm/MC/MCWinCOFFStreamer.h | 2 +- llvm/include/llvm/MC/TargetRegistry.h | 1373 ++++++++++++++++ llvm/include/llvm/MCA/CustomBehaviour.h | 51 +- llvm/include/llvm/MCA/Instruction.h | 4 +- llvm/include/llvm/MCA/Stages/InOrderIssueStage.h | 5 +- llvm/include/llvm/MCA/View.h | 41 + llvm/include/llvm/Object/ELF.h | 7 +- llvm/include/llvm/Object/ELFObjectFile.h | 17 +- llvm/include/llvm/Object/ELFTypes.h | 8 +- llvm/include/llvm/Object/Error.h | 4 + llvm/include/llvm/Object/MachO.h | 3 + llvm/include/llvm/Object/Wasm.h | 9 +- llvm/include/llvm/Object/XCOFFObjectFile.h | 153 +- llvm/include/llvm/ObjectYAML/MachOYAML.h | 1 + llvm/include/llvm/ObjectYAML/WasmYAML.h | 38 +- llvm/include/llvm/ObjectYAML/XCOFFYAML.h | 54 +- llvm/include/llvm/Option/Arg.h | 5 +- llvm/include/llvm/Option/OptParser.td | 2 +- llvm/include/llvm/Option/OptTable.h | 13 +- llvm/include/llvm/Option/Option.h | 14 +- llvm/include/llvm/Passes/OptimizationLevel.h | 127 ++ llvm/include/llvm/Passes/PassBuilder.h | 178 +-- .../include/llvm/Passes/StandardInstrumentations.h | 217 ++- .../llvm/ProfileData/Coverage/CoverageMapping.h | 7 +- llvm/include/llvm/ProfileData/InstrProf.h | 18 +- llvm/include/llvm/ProfileData/InstrProfData.inc | 11 +- llvm/include/llvm/ProfileData/InstrProfReader.h | 18 +- llvm/include/llvm/ProfileData/ProfileCommon.h | 10 +- llvm/include/llvm/ProfileData/SampleProf.h | 376 +++-- llvm/include/llvm/ProfileData/SampleProfReader.h | 49 +- llvm/include/llvm/ProfileData/SampleProfWriter.h | 74 +- llvm/include/llvm/Support/AArch64TargetParser.def | 36 + llvm/include/llvm/Support/ARMTargetParser.def | 18 + llvm/include/llvm/Support/Allocator.h | 2 +- llvm/include/llvm/Support/AtomicOrdering.h | 10 + llvm/include/llvm/Support/BinaryByteStream.h | 34 +- llvm/include/llvm/Support/BinaryItemStream.h | 14 +- llvm/include/llvm/Support/BinaryStream.h | 12 +- llvm/include/llvm/Support/BinaryStreamArray.h | 7 +- llvm/include/llvm/Support/BinaryStreamReader.h | 14 +- llvm/include/llvm/Support/BinaryStreamRef.h | 71 +- llvm/include/llvm/Support/BinaryStreamWriter.h | 14 +- llvm/include/llvm/Support/Caching.h | 71 + llvm/include/llvm/Support/CommandLine.h | 64 +- llvm/include/llvm/Support/Compiler.h | 60 +- llvm/include/llvm/Support/CrashRecoveryContext.h | 3 +- llvm/include/llvm/Support/DOTGraphTraits.h | 5 + llvm/include/llvm/Support/DataExtractor.h | 3 + llvm/include/llvm/Support/Debug.h | 21 - llvm/include/llvm/Support/DivisionByConstantInfo.h | 38 + llvm/include/llvm/Support/Error.h | 37 +- llvm/include/llvm/Support/ErrorHandling.h | 26 +- llvm/include/llvm/Support/ExtensibleRTTI.h | 7 +- llvm/include/llvm/Support/FileSystem.h | 8 +- llvm/include/llvm/Support/FileSystem/UniqueID.h | 27 + llvm/include/llvm/Support/FormatVariadic.h | 2 +- .../llvm/Support/GenericDomTreeConstruction.h | 4 +- llvm/include/llvm/Support/GraphWriter.h | 91 +- llvm/include/llvm/Support/HashBuilder.h | 438 ++++++ llvm/include/llvm/Support/JSON.h | 46 +- llvm/include/llvm/Support/KnownBits.h | 21 +- llvm/include/llvm/Support/MD5.h | 37 +- llvm/include/llvm/Support/MSP430AttributeParser.h | 44 + llvm/include/llvm/Support/MSP430Attributes.h | 44 + llvm/include/llvm/Support/MachineValueType.h | 50 +- llvm/include/llvm/Support/Memory.h | 13 +- llvm/include/llvm/Support/PGOOptions.h | 65 + llvm/include/llvm/Support/Parallel.h | 5 +- llvm/include/llvm/Support/Path.h | 67 +- llvm/include/llvm/Support/Process.h | 6 +- llvm/include/llvm/Support/RISCVISAInfo.h | 89 ++ llvm/include/llvm/Support/RISCVTargetParser.def | 10 +- llvm/include/llvm/Support/Signposts.h | 43 +- llvm/include/llvm/Support/TargetOpcodes.def | 3 + llvm/include/llvm/Support/TargetRegistry.h | 1297 --------------- llvm/include/llvm/Support/TargetSelect.h | 12 + llvm/include/llvm/Support/TypeSize.h | 8 +- llvm/include/llvm/Support/VersionTuple.h | 7 + llvm/include/llvm/Support/VirtualFileSystem.h | 35 +- llvm/include/llvm/Support/Windows/WindowsSupport.h | 4 +- .../llvm/Support/X86DisassemblerDecoderCommon.h | 8 +- llvm/include/llvm/Support/X86TargetParser.def | 135 +- llvm/include/llvm/Support/X86TargetParser.h | 4 + llvm/include/llvm/Support/YAMLTraits.h | 2 +- llvm/include/llvm/Support/raw_ostream.h | 8 +- llvm/include/llvm/TableGen/DirectiveEmitter.h | 2 +- llvm/include/llvm/TableGen/Error.h | 22 +- llvm/include/llvm/TableGen/Record.h | 51 +- llvm/include/llvm/Target/GenericOpcodes.td | 12 + llvm/include/llvm/Target/GlobalISel/Combine.td | 101 +- .../llvm/Target/GlobalISel/SelectionDAGCompat.td | 2 + llvm/include/llvm/Target/Target.td | 22 + .../include/llvm/Target/TargetLoweringObjectFile.h | 9 + llvm/include/llvm/Target/TargetMachine.h | 25 + llvm/include/llvm/Target/TargetOptions.h | 34 +- llvm/include/llvm/Target/TargetSelectionDAG.td | 28 +- llvm/include/llvm/TextAPI/Architecture.h | 6 +- llvm/include/llvm/TextAPI/ArchitectureSet.h | 6 +- llvm/include/llvm/TextAPI/InterfaceFile.h | 8 +- llvm/include/llvm/TextAPI/PackedVersion.h | 6 +- llvm/include/llvm/TextAPI/Platform.h | 6 +- llvm/include/llvm/TextAPI/Symbol.h | 6 +- llvm/include/llvm/TextAPI/Target.h | 6 +- llvm/include/llvm/TextAPI/TextAPIReader.h | 6 +- llvm/include/llvm/TextAPI/TextAPIWriter.h | 6 +- llvm/include/llvm/Transforms/IPO/Attributor.h | 214 ++- llvm/include/llvm/Transforms/IPO/FunctionAttrs.h | 8 + llvm/include/llvm/Transforms/IPO/FunctionImport.h | 29 +- llvm/include/llvm/Transforms/IPO/IROutliner.h | 36 +- llvm/include/llvm/Transforms/IPO/Inliner.h | 16 +- llvm/include/llvm/Transforms/IPO/LoopExtractor.h | 2 + llvm/include/llvm/Transforms/IPO/ModuleInliner.h | 51 + .../llvm/Transforms/IPO/PassManagerBuilder.h | 1 - .../llvm/Transforms/IPO/ProfiledCallGraph.h | 13 +- .../llvm/Transforms/IPO/SampleContextTracker.h | 55 +- .../llvm/Transforms/InstCombine/InstCombine.h | 10 +- .../Transforms/InstCombine/InstCombineWorklist.h | 128 -- .../llvm/Transforms/InstCombine/InstCombiner.h | 50 +- llvm/include/llvm/Transforms/Instrumentation.h | 6 +- .../Transforms/Instrumentation/AddressSanitizer.h | 48 +- .../Instrumentation/AddressSanitizerCommon.h | 79 +- .../Instrumentation/AddressSanitizerOptions.h | 7 +- .../Instrumentation/HWAddressSanitizer.h | 30 +- .../Transforms/Instrumentation/InstrOrderFile.h | 7 +- .../llvm/Transforms/Instrumentation/MemProfiler.h | 7 +- .../Transforms/Instrumentation/MemorySanitizer.h | 17 + .../Transforms/Instrumentation/ThreadSanitizer.h | 8 + llvm/include/llvm/Transforms/Scalar/EarlyCSE.h | 2 + llvm/include/llvm/Transforms/Scalar/GVN.h | 15 +- .../include/llvm/Transforms/Scalar/JumpThreading.h | 8 +- .../llvm/Transforms/Scalar/LoopPassManager.h | 94 +- .../llvm/Transforms/Scalar/LoopUnrollPass.h | 2 + .../llvm/Transforms/Scalar/LowerMatrixIntrinsics.h | 2 + .../llvm/Transforms/Scalar/MemCpyOptimizer.h | 9 +- .../llvm/Transforms/Scalar/MergedLoadStoreMotion.h | 2 + llvm/include/llvm/Transforms/Scalar/SROA.h | 4 +- .../llvm/Transforms/Scalar/SimpleLoopUnswitch.h | 3 + llvm/include/llvm/Transforms/Scalar/SimplifyCFG.h | 3 + .../llvm/Transforms/Utils/ASanStackFrameLayout.h | 12 +- .../llvm/Transforms/Utils/AddDiscriminators.h | 1 + .../llvm/Transforms/Utils/BasicBlockUtils.h | 30 +- llvm/include/llvm/Transforms/Utils/BuildLibCalls.h | 10 +- llvm/include/llvm/Transforms/Utils/Cloning.h | 4 +- llvm/include/llvm/Transforms/Utils/CodeExtractor.h | 18 + .../include/llvm/Transforms/Utils/CodeMoverUtils.h | 16 +- .../llvm/Transforms/Utils/EntryExitInstrumenter.h | 3 + .../llvm/Transforms/Utils/FunctionImportUtils.h | 3 - llvm/include/llvm/Transforms/Utils/GlobalStatus.h | 15 +- .../llvm/Transforms/Utils/InstructionWorklist.h | 123 ++ llvm/include/llvm/Transforms/Utils/Local.h | 40 +- llvm/include/llvm/Transforms/Utils/LoopPeel.h | 4 +- llvm/include/llvm/Transforms/Utils/LoopUtils.h | 56 +- .../include/llvm/Transforms/Utils/MemoryOpRemark.h | 7 +- llvm/include/llvm/Transforms/Utils/PredicateInfo.h | 6 +- .../include/llvm/Transforms/Utils/SSAUpdaterBulk.h | 4 - .../Transforms/Utils/SampleProfileLoaderBaseImpl.h | 70 +- .../Transforms/Utils/ScalarEvolutionExpander.h | 9 +- .../llvm/Transforms/Utils/SimplifyLibCalls.h | 2 - llvm/include/llvm/Transforms/Utils/UnrollLoop.h | 3 +- llvm/include/llvm/Transforms/Utils/ValueMapper.h | 11 +- .../Vectorize/LoopVectorizationLegality.h | 2 +- .../llvm/Transforms/Vectorize/LoopVectorize.h | 2 + .../llvm/Transforms/Vectorize/SLPVectorizer.h | 4 +- .../llvm/Transforms/Vectorize/VectorCombine.h | 10 +- .../llvm/WindowsManifest/WindowsManifestMerger.h | 3 +- llvm/include/llvm/module.modulemap | 18 +- 487 files changed, 18877 insertions(+), 13196 deletions(-) create mode 100644 llvm/include/llvm/ADT/CombinationGenerator.h create mode 100644 llvm/include/llvm/Analysis/CostModel.h create mode 100644 llvm/include/llvm/Analysis/InlineOrder.h create mode 100644 llvm/include/llvm/CodeGen/CodeGenCommonISel.h create mode 100644 llvm/include/llvm/CodeGen/GlobalISel/LoadStoreOpt.h create mode 100644 llvm/include/llvm/CodeGen/MIRSampleProfile.h create mode 100644 llvm/include/llvm/ExecutionEngine/JITLink/ELF_aarch64.h create mode 100644 llvm/include/llvm/ExecutionEngine/JITLink/MemoryFlags.h create mode 100644 llvm/include/llvm/ExecutionEngine/JITLink/TableManager.h create mode 100644 llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h create mode 100644 llvm/include/llvm/ExecutionEngine/Orc/DebuggerSupportPlugin.h create mode 100644 llvm/include/llvm/ExecutionEngine/Orc/ELFNixPlatform.h create mode 100644 llvm/include/llvm/ExecutionEngine/Orc/EPCGenericDylibManager.h create mode 100644 llvm/include/llvm/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.h create mode 100644 llvm/include/llvm/ExecutionEngine/Orc/EPCGenericMemoryAccess.h create mode 100644 llvm/include/llvm/ExecutionEngine/Orc/EPCGenericRTDyldMemoryManager.h delete mode 100644 llvm/include/llvm/ExecutionEngine/Orc/LLVMSPSSerializers.h create mode 100644 llvm/include/llvm/ExecutionEngine/Orc/LookupAndRecordAddrs.h delete mode 100644 llvm/include/llvm/ExecutionEngine/Orc/OrcRPCExecutorProcessControl.h delete mode 100644 llvm/include/llvm/ExecutionEngine/Orc/OrcRemoteTargetClient.h delete mode 100644 llvm/include/llvm/ExecutionEngine/Orc/OrcRemoteTargetRPCAPI.h delete mode 100644 llvm/include/llvm/ExecutionEngine/Orc/OrcRemoteTargetServer.h delete mode 100644 llvm/include/llvm/ExecutionEngine/Orc/Shared/FDRawByteChannel.h create mode 100644 llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h delete mode 100644 llvm/include/llvm/ExecutionEngine/Orc/Shared/RPCUtils.h delete mode 100644 llvm/include/llvm/ExecutionEngine/Orc/Shared/RawByteChannel.h delete mode 100644 llvm/include/llvm/ExecutionEngine/Orc/Shared/Serialization.h create mode 100644 llvm/include/llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h create mode 100644 llvm/include/llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h create mode 100644 llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/ExecutorBootstrapService.h delete mode 100644 llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/OrcRPCTPCServer.h create mode 100644 llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/SimpleExecutorDylibManager.h create mode 100644 llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/SimpleExecutorMemoryManager.h create mode 100644 llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/SimpleRemoteEPCServer.h create mode 100644 llvm/include/llvm/ExecutionEngine/Orc/TaskDispatch.h delete mode 100644 llvm/include/llvm/ExecutionEngine/OrcMCJITReplacement.h delete mode 100644 llvm/include/llvm/ExecutionEngine/OrcV1Deprecation.h delete mode 100644 llvm/include/llvm/IR/GlobalIndirectSymbol.h delete mode 100644 llvm/include/llvm/LTO/Caching.h create mode 100644 llvm/include/llvm/MC/MCAsmInfoGOFF.h create mode 100644 llvm/include/llvm/MC/TargetRegistry.h create mode 100644 llvm/include/llvm/MCA/View.h create mode 100644 llvm/include/llvm/Passes/OptimizationLevel.h create mode 100644 llvm/include/llvm/Support/Caching.h create mode 100644 llvm/include/llvm/Support/DivisionByConstantInfo.h create mode 100644 llvm/include/llvm/Support/HashBuilder.h create mode 100644 llvm/include/llvm/Support/MSP430AttributeParser.h create mode 100644 llvm/include/llvm/Support/MSP430Attributes.h create mode 100644 llvm/include/llvm/Support/PGOOptions.h create mode 100644 llvm/include/llvm/Support/RISCVISAInfo.h delete mode 100644 llvm/include/llvm/Support/TargetRegistry.h create mode 100644 llvm/include/llvm/Transforms/IPO/ModuleInliner.h delete mode 100644 llvm/include/llvm/Transforms/InstCombine/InstCombineWorklist.h create mode 100644 llvm/include/llvm/Transforms/Utils/InstructionWorklist.h (limited to 'llvm/include') diff --git a/llvm/include/llvm-c/Comdat.h b/llvm/include/llvm-c/Comdat.h index 81cde1107fa4..8002bc0581af 100644 --- a/llvm/include/llvm-c/Comdat.h +++ b/llvm/include/llvm-c/Comdat.h @@ -19,6 +19,13 @@ LLVM_C_EXTERN_C_BEGIN +/** + * @defgroup LLVMCCoreComdat Comdats + * @ingroup LLVMCCore + * + * @{ + */ + typedef enum { LLVMAnyComdatSelectionKind, ///< The linker may choose any COMDAT. LLVMExactMatchComdatSelectionKind, ///< The data referenced by the COMDAT must @@ -66,6 +73,10 @@ LLVMComdatSelectionKind LLVMGetComdatSelectionKind(LLVMComdatRef C); */ void LLVMSetComdatSelectionKind(LLVMComdatRef C, LLVMComdatSelectionKind Kind); +/** + * @} + */ + LLVM_C_EXTERN_C_END #endif diff --git a/llvm/include/llvm-c/Core.h b/llvm/include/llvm-c/Core.h index 1a5e763cfc60..d170eff17951 100644 --- a/llvm/include/llvm-c/Core.h +++ b/llvm/include/llvm-c/Core.h @@ -1580,10 +1580,10 @@ LLVMTypeRef LLVMX86AMXType(void); macro(ConstantVector) \ macro(GlobalValue) \ macro(GlobalAlias) \ - macro(GlobalIFunc) \ macro(GlobalObject) \ macro(Function) \ macro(GlobalVariable) \ + macro(GlobalIFunc) \ macro(UndefValue) \ macro(PoisonValue) \ macro(Instruction) \ @@ -3287,7 +3287,7 @@ void LLVMSetInstructionCallConv(LLVMValueRef Instr, unsigned CC); */ unsigned LLVMGetInstructionCallConv(LLVMValueRef Instr); -void LLVMSetInstrParamAlignment(LLVMValueRef Instr, unsigned index, +void LLVMSetInstrParamAlignment(LLVMValueRef Instr, LLVMAttributeIndex Idx, unsigned Align); void LLVMAddCallSiteAttribute(LLVMValueRef C, LLVMAttributeIndex Idx, @@ -3611,10 +3611,20 @@ void LLVMSetCurrentDebugLocation2(LLVMBuilderRef Builder, LLVMMetadataRef Loc); * current debug location for the given builder. If the builder has no current * debug location, this function is a no-op. * + * @deprecated LLVMSetInstDebugLocation is deprecated in favor of the more general + * LLVMAddMetadataToInst. + * * @see llvm::IRBuilder::SetInstDebugLocation() */ void LLVMSetInstDebugLocation(LLVMBuilderRef Builder, LLVMValueRef Inst); +/** + * Adds the metadata registered with the given builder to the given instruction. + * + * @see llvm::IRBuilder::AddMetadataToInst() + */ +void LLVMAddMetadataToInst(LLVMBuilderRef Builder, LLVMValueRef Inst); + /** * Get the dafult floating-point math metadata for a given builder. * @@ -4081,6 +4091,7 @@ void LLVMDisposeMemoryBuffer(LLVMMemoryBufferRef MemBuf); /** * @defgroup LLVMCCorePassRegistry Pass Registry + * @ingroup LLVMCCore * * @{ */ @@ -4095,6 +4106,7 @@ LLVMPassRegistryRef LLVMGetGlobalPassRegistry(void); /** * @defgroup LLVMCCorePassManagers Pass Managers + * @ingroup LLVMCCore * * @{ */ diff --git a/llvm/include/llvm-c/DebugInfo.h b/llvm/include/llvm-c/DebugInfo.h index 8c085807914b..d7fb898b60d2 100644 --- a/llvm/include/llvm-c/DebugInfo.h +++ b/llvm/include/llvm-c/DebugInfo.h @@ -21,6 +21,13 @@ LLVM_C_EXTERN_C_BEGIN +/** + * @defgroup LLVMCCoreDebugInfo Debug Information + * @ingroup LLVMCCore + * + * @{ + */ + /** * Debug info flags. */ @@ -226,6 +233,13 @@ void LLVMDisposeDIBuilder(LLVMDIBuilderRef Builder); */ void LLVMDIBuilderFinalize(LLVMDIBuilderRef Builder); +/** + * Finalize a specific subprogram. + * No new variables may be added to this subprogram afterwards. + */ +void LLVMDIBuilderFinalizeSubprogram(LLVMDIBuilderRef Builder, + LLVMMetadataRef Subprogram); + /** * A CompileUnit provides an anchor for all debugging * information generated during this instance of compilation. @@ -389,48 +403,48 @@ LLVMDIBuilderCreateImportedModuleFromNamespace(LLVMDIBuilderRef Builder, * \param ImportedEntity Previous imported entity to alias. * \param File File where the declaration is located. * \param Line Line number of the declaration. + * \param Elements Renamed elements. + * \param NumElements Number of renamed elements. */ -LLVMMetadataRef -LLVMDIBuilderCreateImportedModuleFromAlias(LLVMDIBuilderRef Builder, - LLVMMetadataRef Scope, - LLVMMetadataRef ImportedEntity, - LLVMMetadataRef File, - unsigned Line); +LLVMMetadataRef LLVMDIBuilderCreateImportedModuleFromAlias( + LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, + LLVMMetadataRef ImportedEntity, LLVMMetadataRef File, unsigned Line, + LLVMMetadataRef *Elements, unsigned NumElements); /** * Create a descriptor for an imported module. - * \param Builder The \c DIBuilder. - * \param Scope The scope this module is imported into - * \param M The module being imported here - * \param File File where the declaration is located. - * \param Line Line number of the declaration. + * \param Builder The \c DIBuilder. + * \param Scope The scope this module is imported into + * \param M The module being imported here + * \param File File where the declaration is located. + * \param Line Line number of the declaration. + * \param Elements Renamed elements. + * \param NumElements Number of renamed elements. */ -LLVMMetadataRef -LLVMDIBuilderCreateImportedModuleFromModule(LLVMDIBuilderRef Builder, - LLVMMetadataRef Scope, - LLVMMetadataRef M, - LLVMMetadataRef File, - unsigned Line); +LLVMMetadataRef LLVMDIBuilderCreateImportedModuleFromModule( + LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, LLVMMetadataRef M, + LLVMMetadataRef File, unsigned Line, LLVMMetadataRef *Elements, + unsigned NumElements); /** * Create a descriptor for an imported function, type, or variable. Suitable * for e.g. FORTRAN-style USE declarations. - * \param Builder The DIBuilder. - * \param Scope The scope this module is imported into. - * \param Decl The declaration (or definition) of a function, type, - or variable. - * \param File File where the declaration is located. - * \param Line Line number of the declaration. - * \param Name A name that uniquely identifies this imported declaration. - * \param NameLen The length of the C string passed to \c Name. + * \param Builder The DIBuilder. + * \param Scope The scope this module is imported into. + * \param Decl The declaration (or definition) of a function, type, + or variable. + * \param File File where the declaration is located. + * \param Line Line number of the declaration. + * \param Name A name that uniquely identifies this imported + declaration. + * \param NameLen The length of the C string passed to \c Name. + * \param Elements Renamed elements. + * \param NumElements Number of renamed elements. */ -LLVMMetadataRef -LLVMDIBuilderCreateImportedDeclaration(LLVMDIBuilderRef Builder, - LLVMMetadataRef Scope, - LLVMMetadataRef Decl, - LLVMMetadataRef File, - unsigned Line, - const char *Name, size_t NameLen); +LLVMMetadataRef LLVMDIBuilderCreateImportedDeclaration( + LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, LLVMMetadataRef Decl, + LLVMMetadataRef File, unsigned Line, const char *Name, size_t NameLen, + LLVMMetadataRef *Elements, unsigned NumElements); /** * Creates a new DebugLocation that describes a source location. @@ -1360,6 +1374,10 @@ void LLVMInstructionSetDebugLoc(LLVMValueRef Inst, LLVMMetadataRef Loc); */ LLVMMetadataKind LLVMGetMetadataKind(LLVMMetadataRef Metadata); +/** + * @} + */ + LLVM_C_EXTERN_C_END #endif diff --git a/llvm/include/llvm-c/DisassemblerTypes.h b/llvm/include/llvm-c/DisassemblerTypes.h index ae5c68227594..53baaef11033 100644 --- a/llvm/include/llvm-c/DisassemblerTypes.h +++ b/llvm/include/llvm-c/DisassemblerTypes.h @@ -17,6 +17,12 @@ #include #endif +/** + * @addtogroup LLVMCDisassembler + * + * @{ + */ + /** * An opaque reference to a disassembler context. */ @@ -157,4 +163,8 @@ typedef const char *(*LLVMSymbolLookupCallback)(void *DisInfo, /* The output reference is to a C++ symbol name. */ #define LLVMDisassembler_ReferenceType_DeMangled_Name 9 +/** + * @} + */ + #endif diff --git a/llvm/include/llvm-c/Error.h b/llvm/include/llvm-c/Error.h index bc702ac7a1bf..c3baaf65186a 100644 --- a/llvm/include/llvm-c/Error.h +++ b/llvm/include/llvm-c/Error.h @@ -18,6 +18,13 @@ LLVM_C_EXTERN_C_BEGIN +/** + * @defgroup LLVMCError Error Handling + * @ingroup LLVMC + * + * @{ + */ + #define LLVMErrorSuccess 0 /** @@ -67,6 +74,10 @@ LLVMErrorTypeId LLVMGetStringErrorTypeId(void); */ LLVMErrorRef LLVMCreateStringError(const char *ErrMsg); +/** + * @} + */ + LLVM_C_EXTERN_C_END #endif diff --git a/llvm/include/llvm-c/ErrorHandling.h b/llvm/include/llvm-c/ErrorHandling.h index 5ba099c209c0..d9b9f22752b8 100644 --- a/llvm/include/llvm-c/ErrorHandling.h +++ b/llvm/include/llvm-c/ErrorHandling.h @@ -18,6 +18,12 @@ LLVM_C_EXTERN_C_BEGIN +/** + * @addtogroup LLVMCError + * + * @{ + */ + typedef void (*LLVMFatalErrorHandler)(const char *Reason); /** @@ -42,6 +48,10 @@ void LLVMResetFatalErrorHandler(void); */ void LLVMEnablePrettyStackTrace(void); +/** + * @} + */ + LLVM_C_EXTERN_C_END #endif diff --git a/llvm/include/llvm-c/IRReader.h b/llvm/include/llvm-c/IRReader.h index 5a3f633c3d91..905b84fa5a86 100644 --- a/llvm/include/llvm-c/IRReader.h +++ b/llvm/include/llvm-c/IRReader.h @@ -19,6 +19,13 @@ LLVM_C_EXTERN_C_BEGIN +/** + * @defgroup LLVMCCoreIRReader IR Reader + * @ingroup LLVMCCore + * + * @{ + */ + /** * Read LLVM IR from a memory buffer and convert it into an in-memory Module * object. Returns 0 on success. @@ -32,6 +39,10 @@ LLVMBool LLVMParseIRInContext(LLVMContextRef ContextRef, LLVMMemoryBufferRef MemBuf, LLVMModuleRef *OutM, char **OutMessage); +/** + * @} + */ + LLVM_C_EXTERN_C_END #endif diff --git a/llvm/include/llvm-c/LLJIT.h b/llvm/include/llvm-c/LLJIT.h index f689ca0f1cf0..a06133aac4fb 100644 --- a/llvm/include/llvm-c/LLJIT.h +++ b/llvm/include/llvm-c/LLJIT.h @@ -31,6 +31,13 @@ LLVM_C_EXTERN_C_BEGIN +/** + * @defgroup LLVMCExecutionEngineLLJIT LLJIT + * @ingroup LLVMCExecutionEngine + * + * @{ + */ + /** * A function for constructing an ObjectLinkingLayer instance to be used * by an LLJIT instance. @@ -235,6 +242,10 @@ LLVMOrcIRTransformLayerRef LLVMOrcLLJITGetIRTransformLayer(LLVMOrcLLJITRef J); */ const char *LLVMOrcLLJITGetDataLayoutStr(LLVMOrcLLJITRef J); +/** + * @} + */ + LLVM_C_EXTERN_C_END #endif /* LLVM_C_LLJIT_H */ diff --git a/llvm/include/llvm-c/Linker.h b/llvm/include/llvm-c/Linker.h index 1ad9cc958753..acff5d5e2225 100644 --- a/llvm/include/llvm-c/Linker.h +++ b/llvm/include/llvm-c/Linker.h @@ -19,6 +19,13 @@ LLVM_C_EXTERN_C_BEGIN +/** + * @defgroup LLVMCCoreLinker Linker + * @ingroup LLVMCCore + * + * @{ + */ + /* This enum is provided for backwards-compatibility only. It has no effect. */ typedef enum { LLVMLinkerDestroySource = 0, /* This is the default behavior. */ @@ -35,4 +42,8 @@ LLVMBool LLVMLinkModules2(LLVMModuleRef Dest, LLVMModuleRef Src); LLVM_C_EXTERN_C_END +/** + * @} + */ + #endif diff --git a/llvm/include/llvm-c/Orc.h b/llvm/include/llvm-c/Orc.h index 1790afbcecc7..e2f30b7cdf45 100644 --- a/llvm/include/llvm-c/Orc.h +++ b/llvm/include/llvm-c/Orc.h @@ -33,6 +33,13 @@ LLVM_C_EXTERN_C_BEGIN +/** + * @defgroup LLVMCExecutionEngineORC On-Request-Compilation + * @ingroup LLVMCExecutionEngine + * + * @{ + */ + /** * Represents an address in the executor process. */ @@ -920,6 +927,49 @@ LLVMErrorRef LLVMOrcCreateDynamicLibrarySearchGeneratorForProcess( LLVMOrcDefinitionGeneratorRef *Result, char GlobalPrefx, LLVMOrcSymbolPredicate Filter, void *FilterCtx); +/** + * Get a LLVMOrcCreateDynamicLibararySearchGeneratorForPath that will reflect + * library symbols into the JITDylib. On success the resulting generator is + * owned by the client. Ownership is typically transferred by adding the + * instance to a JITDylib using LLVMOrcJITDylibAddGenerator, + * + * The GlobalPrefix argument specifies the character that appears on the front + * of linker-mangled symbols for the target platform (e.g. '_' on MachO). + * If non-null, this character will be stripped from the start of all symbol + * strings before passing the remaining substring to dlsym. + * + * The optional Filter and Ctx arguments can be used to supply a symbol name + * filter: Only symbols for which the filter returns true will be visible to + * JIT'd code. If the Filter argument is null then all library symbols will + * be visible to JIT'd code. Note that the symbol name passed to the Filter + * function is the full mangled symbol: The client is responsible for stripping + * the global prefix if present. + * + * THIS API IS EXPERIMENTAL AND LIKELY TO CHANGE IN THE NEAR FUTURE! + * + */ +LLVMErrorRef LLVMOrcCreateDynamicLibrarySearchGeneratorForPath( + LLVMOrcDefinitionGeneratorRef *Result, const char *FileName, + char GlobalPrefix, LLVMOrcSymbolPredicate Filter, void *FilterCtx); + +/** + * Get a LLVMOrcCreateStaticLibrarySearchGeneratorForPath that will reflect + * static library symbols into the JITDylib. On success the resulting + * generator is owned by the client. Ownership is typically transferred by + * adding the instance to a JITDylib using LLVMOrcJITDylibAddGenerator, + * + * Call with the optional TargetTriple argument will succeed if the file at + * the given path is a static library or a MachO universal binary containing a + * static library that is compatible with the given triple. Otherwise it will + * return an error. + * + * THIS API IS EXPERIMENTAL AND LIKELY TO CHANGE IN THE NEAR FUTURE! + * + */ +LLVMErrorRef LLVMOrcCreateStaticLibrarySearchGeneratorForPath( + LLVMOrcDefinitionGeneratorRef *Result, LLVMOrcObjectLayerRef ObjLayer, + const char *FileName, const char *TargetTriple); + /** * Create a ThreadSafeContext containing a new LLVMContext. * @@ -1133,6 +1183,10 @@ void LLVMOrcDisposeDumpObjects(LLVMOrcDumpObjectsRef DumpObjects); LLVMErrorRef LLVMOrcDumpObjects_CallOperator(LLVMOrcDumpObjectsRef DumpObjects, LLVMMemoryBufferRef *ObjBuffer); +/** + * @} + */ + LLVM_C_EXTERN_C_END #endif /* LLVM_C_ORC_H */ diff --git a/llvm/include/llvm-c/OrcEE.h b/llvm/include/llvm-c/OrcEE.h index 2435e7421a42..e7ae0f5e6be2 100644 --- a/llvm/include/llvm-c/OrcEE.h +++ b/llvm/include/llvm-c/OrcEE.h @@ -32,6 +32,13 @@ LLVM_C_EXTERN_C_BEGIN +/** + * @defgroup LLVMCExecutionEngineORCEE ExecutionEngine-based ORC Utils + * @ingroup LLVMCExecutionEngine + * + * @{ + */ + /** * Create a RTDyldObjectLinkingLayer instance using the standard * SectionMemoryManager for memory management. @@ -50,6 +57,10 @@ void LLVMOrcRTDyldObjectLinkingLayerRegisterJITEventListener( LLVMOrcObjectLayerRef RTDyldObjLinkingLayer, LLVMJITEventListenerRef Listener); +/** + * @} + */ + LLVM_C_EXTERN_C_END #endif /* LLVM_C_ORCEE_H */ diff --git a/llvm/include/llvm-c/Support.h b/llvm/include/llvm-c/Support.h index 866df32efa98..17657861b32b 100644 --- a/llvm/include/llvm-c/Support.h +++ b/llvm/include/llvm-c/Support.h @@ -20,6 +20,12 @@ LLVM_C_EXTERN_C_BEGIN +/** + * @addtogroup LLVMCCore + * + * @{ + */ + /** * This function permanently loads the dynamic library at the given path. * It is safe to call this function multiple times for the same library. @@ -57,6 +63,10 @@ void *LLVMSearchForAddressOfSymbol(const char *symbolName); */ void LLVMAddSymbol(const char *symbolName, void *symbolValue); +/** + * @} + */ + LLVM_C_EXTERN_C_END #endif diff --git a/llvm/include/llvm-c/TargetMachine.h b/llvm/include/llvm-c/TargetMachine.h index f82edd948b59..23c8c63ff0b4 100644 --- a/llvm/include/llvm-c/TargetMachine.h +++ b/llvm/include/llvm-c/TargetMachine.h @@ -25,6 +25,12 @@ LLVM_C_EXTERN_C_BEGIN +/** + * @addtogroup LLVMCTarget + * + * @{ + */ + typedef struct LLVMOpaqueTargetMachine *LLVMTargetMachineRef; typedef struct LLVMTarget *LLVMTargetRef; @@ -156,6 +162,10 @@ char* LLVMGetHostCPUFeatures(void); /** Adds the target-specific analysis passes to the pass manager. */ void LLVMAddAnalysisPasses(LLVMTargetMachineRef T, LLVMPassManagerRef PM); +/** + * @} + */ + LLVM_C_EXTERN_C_END #endif diff --git a/llvm/include/llvm-c/Transforms/PassBuilder.h b/llvm/include/llvm-c/Transforms/PassBuilder.h index 5635f10d6877..6d9f1b45c707 100644 --- a/llvm/include/llvm-c/Transforms/PassBuilder.h +++ b/llvm/include/llvm-c/Transforms/PassBuilder.h @@ -18,6 +18,13 @@ #include "llvm-c/TargetMachine.h" #include "llvm-c/Types.h" +/** + * @defgroup LLVMCCoreNewPM New Pass Manager + * @ingroup LLVMCCore + * + * @{ + */ + LLVM_C_EXTERN_C_BEGIN /** @@ -50,7 +57,7 @@ LLVMErrorRef LLVMRunPasses(LLVMModuleRef M, const char *Passes, * responsible for it. The client should call LLVMDisposePassBuilderOptions * to free the pass builder options. */ -LLVMPassBuilderOptionsRef LLVMCreatePassBuilderOptions(); +LLVMPassBuilderOptionsRef LLVMCreatePassBuilderOptions(void); /** * Toggle adding the VerifierPass for the PassBuilder, ensuring all functions @@ -97,6 +104,10 @@ void LLVMPassBuilderOptionsSetMergeFunctions(LLVMPassBuilderOptionsRef Options, */ void LLVMDisposePassBuilderOptions(LLVMPassBuilderOptionsRef Options); +/** + * @} + */ + LLVM_C_EXTERN_C_END #endif // LLVM_C_TRANSFORMS_PASSBUILDER_H diff --git a/llvm/include/llvm-c/lto.h b/llvm/include/llvm-c/lto.h index f6fc8588f5f7..5ceb02224d2b 100644 --- a/llvm/include/llvm-c/lto.h +++ b/llvm/include/llvm-c/lto.h @@ -46,7 +46,7 @@ typedef bool lto_bool_t; * @{ */ -#define LTO_API_VERSION 28 +#define LTO_API_VERSION 29 /** * \since prior to LTO_API_VERSION=3 @@ -312,6 +312,16 @@ extern lto_bool_t lto_module_get_macho_cputype(lto_module_t mod, unsigned int *out_cputype, unsigned int *out_cpusubtype); +/** + * This function can be used by the linker to check if a given module has + * any constructor or destructor functions. + * + * Returns true if the module has either the @llvm.global_ctors or the + * @llvm.global_dtors symbol. Otherwise returns false. + * + * \since LTO_API_VERSION=29 + */ +extern lto_bool_t lto_module_has_ctor_dtor(lto_module_t mod); /** * Diagnostic severity. * diff --git a/llvm/include/llvm/ADT/APFloat.h b/llvm/include/llvm/ADT/APFloat.h index f493a03b4b87..40e0e32c77a8 100644 --- a/llvm/include/llvm/ADT/APFloat.h +++ b/llvm/include/llvm/ADT/APFloat.h @@ -961,9 +961,7 @@ public: /// Returns a float which is bitcasted from an all one value int. /// /// \param Semantics - type float semantics - /// \param BitWidth - Select float type - static APFloat getAllOnesValue(const fltSemantics &Semantics, - unsigned BitWidth); + static APFloat getAllOnesValue(const fltSemantics &Semantics); /// Used to insert APFloat objects, or objects that contain APFloat objects, /// into FoldingSets. diff --git a/llvm/include/llvm/ADT/APInt.h b/llvm/include/llvm/ADT/APInt.h index ff586f763e82..595cd94b6b8f 100644 --- a/llvm/include/llvm/ADT/APInt.h +++ b/llvm/include/llvm/ADT/APInt.h @@ -31,7 +31,7 @@ class raw_ostream; template class SmallVectorImpl; template class ArrayRef; template class Optional; -template struct DenseMapInfo; +template struct DenseMapInfo; class APInt; @@ -66,6 +66,11 @@ inline APInt operator-(APInt); /// not. /// * In general, the class tries to follow the style of computation that LLVM /// uses in its IR. This simplifies its use for LLVM. +/// * APInt supports zero-bit-width values, but operations that require bits +/// are not defined on it (e.g. you cannot ask for the sign of a zero-bit +/// integer). This means that operations like zero extension and logical +/// shifts are defined, but sign extension and ashr is not. Zero bit values +/// compare and hash equal to themselves, and countLeadingZeros returns 0. /// class LLVM_NODISCARD APInt { public: @@ -87,176 +92,6 @@ public: static constexpr WordType WORDTYPE_MAX = ~WordType(0); -private: - /// This union is used to store the integer value. When the - /// integer bit-width <= 64, it uses VAL, otherwise it uses pVal. - union { - uint64_t VAL; ///< Used to store the <= 64 bits integer value. - uint64_t *pVal; ///< Used to store the >64 bits integer value. - } U; - - unsigned BitWidth; ///< The number of bits in this APInt. - - friend struct DenseMapInfo; - - friend class APSInt; - - /// Fast internal constructor - /// - /// This constructor is used only internally for speed of construction of - /// temporaries. It is unsafe for general use so it is not public. - APInt(uint64_t *val, unsigned bits) : BitWidth(bits) { - U.pVal = val; - } - - /// Determine which word a bit is in. - /// - /// \returns the word position for the specified bit position. - static unsigned whichWord(unsigned bitPosition) { - return bitPosition / APINT_BITS_PER_WORD; - } - - /// Determine which bit in a word a bit is in. - /// - /// \returns the bit position in a word for the specified bit position - /// in the APInt. - static unsigned whichBit(unsigned bitPosition) { - return bitPosition % APINT_BITS_PER_WORD; - } - - /// Get a single bit mask. - /// - /// \returns a uint64_t with only bit at "whichBit(bitPosition)" set - /// This method generates and returns a uint64_t (word) mask for a single - /// bit at a specific bit position. This is used to mask the bit in the - /// corresponding word. - static uint64_t maskBit(unsigned bitPosition) { - return 1ULL << whichBit(bitPosition); - } - - /// Clear unused high order bits - /// - /// This method is used internally to clear the top "N" bits in the high order - /// word that are not used by the APInt. This is needed after the most - /// significant word is assigned a value to ensure that those bits are - /// zero'd out. - APInt &clearUnusedBits() { - // Compute how many bits are used in the final word - unsigned WordBits = ((BitWidth-1) % APINT_BITS_PER_WORD) + 1; - - // Mask out the high bits. - uint64_t mask = WORDTYPE_MAX >> (APINT_BITS_PER_WORD - WordBits); - if (isSingleWord()) - U.VAL &= mask; - else - U.pVal[getNumWords() - 1] &= mask; - return *this; - } - - /// Get the word corresponding to a bit position - /// \returns the corresponding word for the specified bit position. - uint64_t getWord(unsigned bitPosition) const { - return isSingleWord() ? U.VAL : U.pVal[whichWord(bitPosition)]; - } - - /// Utility method to change the bit width of this APInt to new bit width, - /// allocating and/or deallocating as necessary. There is no guarantee on the - /// value of any bits upon return. Caller should populate the bits after. - void reallocate(unsigned NewBitWidth); - - /// Convert a char array into an APInt - /// - /// \param radix 2, 8, 10, 16, or 36 - /// Converts a string into a number. The string must be non-empty - /// and well-formed as a number of the given base. The bit-width - /// must be sufficient to hold the result. - /// - /// This is used by the constructors that take string arguments. - /// - /// StringRef::getAsInteger is superficially similar but (1) does - /// not assume that the string is well-formed and (2) grows the - /// result to hold the input. - void fromString(unsigned numBits, StringRef str, uint8_t radix); - - /// An internal division function for dividing APInts. - /// - /// This is used by the toString method to divide by the radix. It simply - /// provides a more convenient form of divide for internal use since KnuthDiv - /// has specific constraints on its inputs. If those constraints are not met - /// then it provides a simpler form of divide. - static void divide(const WordType *LHS, unsigned lhsWords, - const WordType *RHS, unsigned rhsWords, WordType *Quotient, - WordType *Remainder); - - /// out-of-line slow case for inline constructor - void initSlowCase(uint64_t val, bool isSigned); - - /// shared code between two array constructors - void initFromArray(ArrayRef array); - - /// out-of-line slow case for inline copy constructor - void initSlowCase(const APInt &that); - - /// out-of-line slow case for shl - void shlSlowCase(unsigned ShiftAmt); - - /// out-of-line slow case for lshr. - void lshrSlowCase(unsigned ShiftAmt); - - /// out-of-line slow case for ashr. - void ashrSlowCase(unsigned ShiftAmt); - - /// out-of-line slow case for operator= - void AssignSlowCase(const APInt &RHS); - - /// out-of-line slow case for operator== - bool EqualSlowCase(const APInt &RHS) const LLVM_READONLY; - - /// out-of-line slow case for countLeadingZeros - unsigned countLeadingZerosSlowCase() const LLVM_READONLY; - - /// out-of-line slow case for countLeadingOnes. - unsigned countLeadingOnesSlowCase() const LLVM_READONLY; - - /// out-of-line slow case for countTrailingZeros. - unsigned countTrailingZerosSlowCase() const LLVM_READONLY; - - /// out-of-line slow case for countTrailingOnes - unsigned countTrailingOnesSlowCase() const LLVM_READONLY; - - /// out-of-line slow case for countPopulation - unsigned countPopulationSlowCase() const LLVM_READONLY; - - /// out-of-line slow case for intersects. - bool intersectsSlowCase(const APInt &RHS) const LLVM_READONLY; - - /// out-of-line slow case for isSubsetOf. - bool isSubsetOfSlowCase(const APInt &RHS) const LLVM_READONLY; - - /// out-of-line slow case for setBits. - void setBitsSlowCase(unsigned loBit, unsigned hiBit); - - /// out-of-line slow case for flipAllBits. - void flipAllBitsSlowCase(); - - /// out-of-line slow case for operator&=. - void AndAssignSlowCase(const APInt& RHS); - - /// out-of-line slow case for operator|=. - void OrAssignSlowCase(const APInt& RHS); - - /// out-of-line slow case for operator^=. - void XorAssignSlowCase(const APInt& RHS); - - /// Unsigned comparison. Returns -1, 0, or 1 if this APInt is less than, equal - /// to, or greater than RHS. - int compare(const APInt &RHS) const LLVM_READONLY; - - /// Signed comparison. Returns -1, 0, or 1 if this APInt is less than, equal - /// to, or greater than RHS. - int compareSigned(const APInt &RHS) const LLVM_READONLY; - -public: /// \name Constructors /// @{ @@ -272,7 +107,6 @@ public: /// \param isSigned how to treat signedness of val APInt(unsigned numBits, uint64_t val, bool isSigned = false) : BitWidth(numBits) { - assert(BitWidth && "bitwidth too small"); if (isSingleWord()) { U.VAL = val; clearUnusedBits(); @@ -312,7 +146,9 @@ public: /// \param radix the radix to use for the conversion APInt(unsigned numBits, StringRef str, uint8_t radix); - /// Simply makes *this a copy of that. + /// Default constructor that creates an APInt with a 1-bit zero value. + explicit APInt() : BitWidth(1) { U.VAL = 0; } + /// Copy Constructor. APInt(const APInt &that) : BitWidth(that.BitWidth) { if (isSingleWord()) @@ -333,19 +169,131 @@ public: delete[] U.pVal; } - /// Default constructor that creates an uninteresting APInt - /// representing a 1-bit zero value. + /// @} + /// \name Value Generators + /// @{ + + /// Get the '0' value for the specified bit-width. + static APInt getZero(unsigned numBits) { return APInt(numBits, 0); } + + /// NOTE: This is soft-deprecated. Please use `getZero()` instead. + static APInt getNullValue(unsigned numBits) { return getZero(numBits); } + + /// Return an APInt zero bits wide. + static APInt getZeroWidth() { return getZero(0); } + + /// Gets maximum unsigned value of APInt for specific bit width. + static APInt getMaxValue(unsigned numBits) { return getAllOnes(numBits); } + + /// Gets maximum signed value of APInt for a specific bit width. + static APInt getSignedMaxValue(unsigned numBits) { + APInt API = getAllOnes(numBits); + API.clearBit(numBits - 1); + return API; + } + + /// Gets minimum unsigned value of APInt for a specific bit width. + static APInt getMinValue(unsigned numBits) { return APInt(numBits, 0); } + + /// Gets minimum signed value of APInt for a specific bit width. + static APInt getSignedMinValue(unsigned numBits) { + APInt API(numBits, 0); + API.setBit(numBits - 1); + return API; + } + + /// Get the SignMask for a specific bit width. /// - /// This is useful for object deserialization (pair this with the static - /// method Read). - explicit APInt() : BitWidth(1) { U.VAL = 0; } + /// This is just a wrapper function of getSignedMinValue(), and it helps code + /// readability when we want to get a SignMask. + static APInt getSignMask(unsigned BitWidth) { + return getSignedMinValue(BitWidth); + } - /// Returns whether this instance allocated memory. - bool needsCleanup() const { return !isSingleWord(); } + /// Return an APInt of a specified width with all bits set. + static APInt getAllOnes(unsigned numBits) { + return APInt(numBits, WORDTYPE_MAX, true); + } - /// Used to insert APInt objects, or objects that contain APInt objects, into - /// FoldingSets. - void Profile(FoldingSetNodeID &id) const; + /// NOTE: This is soft-deprecated. Please use `getAllOnes()` instead. + static APInt getAllOnesValue(unsigned numBits) { return getAllOnes(numBits); } + + /// Return an APInt with exactly one bit set in the result. + static APInt getOneBitSet(unsigned numBits, unsigned BitNo) { + APInt Res(numBits, 0); + Res.setBit(BitNo); + return Res; + } + + /// Get a value with a block of bits set. + /// + /// Constructs an APInt value that has a contiguous range of bits set. The + /// bits from loBit (inclusive) to hiBit (exclusive) will be set. All other + /// bits will be zero. For example, with parameters(32, 0, 16) you would get + /// 0x0000FFFF. Please call getBitsSetWithWrap if \p loBit may be greater than + /// \p hiBit. + /// + /// \param numBits the intended bit width of the result + /// \param loBit the index of the lowest bit set. + /// \param hiBit the index of the highest bit set. + /// + /// \returns An APInt value with the requested bits set. + static APInt getBitsSet(unsigned numBits, unsigned loBit, unsigned hiBit) { + APInt Res(numBits, 0); + Res.setBits(loBit, hiBit); + return Res; + } + + /// Wrap version of getBitsSet. + /// If \p hiBit is bigger than \p loBit, this is same with getBitsSet. + /// If \p hiBit is not bigger than \p loBit, the set bits "wrap". For example, + /// with parameters (32, 28, 4), you would get 0xF000000F. + /// If \p hiBit is equal to \p loBit, you would get a result with all bits + /// set. + static APInt getBitsSetWithWrap(unsigned numBits, unsigned loBit, + unsigned hiBit) { + APInt Res(numBits, 0); + Res.setBitsWithWrap(loBit, hiBit); + return Res; + } + + /// Constructs an APInt value that has a contiguous range of bits set. The + /// bits from loBit (inclusive) to numBits (exclusive) will be set. All other + /// bits will be zero. For example, with parameters(32, 12) you would get + /// 0xFFFFF000. + /// + /// \param numBits the intended bit width of the result + /// \param loBit the index of the lowest bit to set. + /// + /// \returns An APInt value with the requested bits set. + static APInt getBitsSetFrom(unsigned numBits, unsigned loBit) { + APInt Res(numBits, 0); + Res.setBitsFrom(loBit); + return Res; + } + + /// Constructs an APInt value that has the top hiBitsSet bits set. + /// + /// \param numBits the bitwidth of the result + /// \param hiBitsSet the number of high-order bits set in the result. + static APInt getHighBitsSet(unsigned numBits, unsigned hiBitsSet) { + APInt Res(numBits, 0); + Res.setHighBits(hiBitsSet); + return Res; + } + + /// Constructs an APInt value that has the bottom loBitsSet bits set. + /// + /// \param numBits the bitwidth of the result + /// \param loBitsSet the number of low-order bits set in the result. + static APInt getLowBitsSet(unsigned numBits, unsigned loBitsSet) { + APInt Res(numBits, 0); + Res.setLowBits(loBitsSet); + return Res; + } + + /// Return a value containing V broadcasted over NewLen bits. + static APInt getSplat(unsigned NewLen, const APInt &V); /// @} /// \name Value Tests @@ -373,7 +321,7 @@ public: /// This tests the high bit of this APInt to determine if it is set. /// /// \returns true if this APInt has its sign bit set, false otherwise. - bool isSignBitSet() const { return (*this)[BitWidth-1]; } + bool isSignBitSet() const { return (*this)[BitWidth - 1]; } /// Determine if sign bit of this APInt is clear. /// @@ -388,50 +336,62 @@ public: /// that 0 is not a positive value. /// /// \returns true if this APInt is positive. - bool isStrictlyPositive() const { return isNonNegative() && !isNullValue(); } + bool isStrictlyPositive() const { return isNonNegative() && !isZero(); } /// Determine if this APInt Value is non-positive (<= 0). /// /// \returns true if this APInt is non-positive. bool isNonPositive() const { return !isStrictlyPositive(); } - /// Determine if all bits are set - /// - /// This checks to see if the value has all bits of the APInt are set or not. - bool isAllOnesValue() const { + /// Determine if all bits are set. This is true for zero-width values. + bool isAllOnes() const { + if (BitWidth == 0) + return true; if (isSingleWord()) return U.VAL == WORDTYPE_MAX >> (APINT_BITS_PER_WORD - BitWidth); return countTrailingOnesSlowCase() == BitWidth; } - /// Determine if all bits are clear - /// - /// This checks to see if the value has all bits of the APInt are clear or - /// not. - bool isNullValue() const { return !*this; } + /// NOTE: This is soft-deprecated. Please use `isAllOnes()` instead. + bool isAllOnesValue() const { return isAllOnes(); } + + /// Determine if this value is zero, i.e. all bits are clear. + bool isZero() const { + if (isSingleWord()) + return U.VAL == 0; + return countLeadingZerosSlowCase() == BitWidth; + } + + /// NOTE: This is soft-deprecated. Please use `isZero()` instead. + bool isNullValue() const { return isZero(); } /// Determine if this is a value of 1. /// /// This checks to see if the value of this APInt is one. - bool isOneValue() const { + bool isOne() const { if (isSingleWord()) return U.VAL == 1; return countLeadingZerosSlowCase() == BitWidth - 1; } + /// NOTE: This is soft-deprecated. Please use `isOne()` instead. + bool isOneValue() const { return isOne(); } + /// Determine if this is the largest unsigned value. /// /// This checks to see if the value of this APInt is the maximum unsigned /// value for the APInt's bit width. - bool isMaxValue() const { return isAllOnesValue(); } + bool isMaxValue() const { return isAllOnes(); } /// Determine if this is the largest signed value. /// /// This checks to see if the value of this APInt is the maximum signed /// value for the APInt's bit width. bool isMaxSignedValue() const { - if (isSingleWord()) + if (isSingleWord()) { + assert(BitWidth && "zero width values not allowed"); return U.VAL == ((WordType(1) << (BitWidth - 1)) - 1); + } return !isNegative() && countTrailingOnesSlowCase() == BitWidth - 1; } @@ -439,39 +399,48 @@ public: /// /// This checks to see if the value of this APInt is the minimum unsigned /// value for the APInt's bit width. - bool isMinValue() const { return isNullValue(); } + bool isMinValue() const { return isZero(); } /// Determine if this is the smallest signed value. /// /// This checks to see if the value of this APInt is the minimum signed /// value for the APInt's bit width. bool isMinSignedValue() const { - if (isSingleWord()) + if (isSingleWord()) { + assert(BitWidth && "zero width values not allowed"); return U.VAL == (WordType(1) << (BitWidth - 1)); + } return isNegative() && countTrailingZerosSlowCase() == BitWidth - 1; } /// Check if this APInt has an N-bits unsigned integer value. - bool isIntN(unsigned N) const { - assert(N && "N == 0 ???"); - return getActiveBits() <= N; - } + bool isIntN(unsigned N) const { return getActiveBits() <= N; } /// Check if this APInt has an N-bits signed integer value. - bool isSignedIntN(unsigned N) const { - assert(N && "N == 0 ???"); - return getMinSignedBits() <= N; - } + bool isSignedIntN(unsigned N) const { return getMinSignedBits() <= N; } /// Check if this APInt's value is a power of two greater than zero. /// /// \returns true if the argument APInt value is a power of two > 0. bool isPowerOf2() const { - if (isSingleWord()) + if (isSingleWord()) { + assert(BitWidth && "zero width values not allowed"); return isPowerOf2_64(U.VAL); + } return countPopulationSlowCase() == 1; } + /// Check if this APInt's negated value is a power of two greater than zero. + bool isNegatedPowerOf2() const { + assert(BitWidth && "zero width values not allowed"); + if (isNonNegative()) + return false; + // NegatedPowerOf2 - shifted mask in the top bits. + unsigned LO = countLeadingOnes(); + unsigned TZ = countTrailingZeros(); + return (LO + TZ) == BitWidth; + } + /// Check if the APInt's value is returned by getSignMask. /// /// \returns true if this is the value returned by getSignMask. @@ -480,7 +449,7 @@ public: /// Convert APInt to a boolean value. /// /// This converts the APInt to a boolean value as a test against zero. - bool getBoolValue() const { return !!*this; } + bool getBoolValue() const { return !isZero(); } /// If this value is smaller than the specified limit, return it, otherwise /// return the limit value. This causes the value to saturate to the limit. @@ -503,175 +472,45 @@ public: if (isSingleWord()) return U.VAL == (WORDTYPE_MAX >> (APINT_BITS_PER_WORD - numBits)); unsigned Ones = countTrailingOnesSlowCase(); - return (numBits == Ones) && - ((Ones + countLeadingZerosSlowCase()) == BitWidth); - } - - /// \returns true if this APInt is a non-empty sequence of ones starting at - /// the least significant bit with the remainder zero. - /// Ex. isMask(0x0000FFFFU) == true. - bool isMask() const { - if (isSingleWord()) - return isMask_64(U.VAL); - unsigned Ones = countTrailingOnesSlowCase(); - return (Ones > 0) && ((Ones + countLeadingZerosSlowCase()) == BitWidth); - } - - /// Return true if this APInt value contains a sequence of ones with - /// the remainder zero. - bool isShiftedMask() const { - if (isSingleWord()) - return isShiftedMask_64(U.VAL); - unsigned Ones = countPopulationSlowCase(); - unsigned LeadZ = countLeadingZerosSlowCase(); - return (Ones + LeadZ + countTrailingZeros()) == BitWidth; - } - - /// @} - /// \name Value Generators - /// @{ - - /// Gets maximum unsigned value of APInt for specific bit width. - static APInt getMaxValue(unsigned numBits) { - return getAllOnesValue(numBits); - } - - /// Gets maximum signed value of APInt for a specific bit width. - static APInt getSignedMaxValue(unsigned numBits) { - APInt API = getAllOnesValue(numBits); - API.clearBit(numBits - 1); - return API; - } - - /// Gets minimum unsigned value of APInt for a specific bit width. - static APInt getMinValue(unsigned numBits) { return APInt(numBits, 0); } - - /// Gets minimum signed value of APInt for a specific bit width. - static APInt getSignedMinValue(unsigned numBits) { - APInt API(numBits, 0); - API.setBit(numBits - 1); - return API; - } - - /// Get the SignMask for a specific bit width. - /// - /// This is just a wrapper function of getSignedMinValue(), and it helps code - /// readability when we want to get a SignMask. - static APInt getSignMask(unsigned BitWidth) { - return getSignedMinValue(BitWidth); - } - - /// Get the all-ones value. - /// - /// \returns the all-ones value for an APInt of the specified bit-width. - static APInt getAllOnesValue(unsigned numBits) { - return APInt(numBits, WORDTYPE_MAX, true); - } - - /// Get the '0' value. - /// - /// \returns the '0' value for an APInt of the specified bit-width. - static APInt getNullValue(unsigned numBits) { return APInt(numBits, 0); } - - /// Compute an APInt containing numBits highbits from this APInt. - /// - /// Get an APInt with the same BitWidth as this APInt, just zero mask - /// the low bits and right shift to the least significant bit. - /// - /// \returns the high "numBits" bits of this APInt. - APInt getHiBits(unsigned numBits) const; - - /// Compute an APInt containing numBits lowbits from this APInt. - /// - /// Get an APInt with the same BitWidth as this APInt, just zero mask - /// the high bits. - /// - /// \returns the low "numBits" bits of this APInt. - APInt getLoBits(unsigned numBits) const; - - /// Return an APInt with exactly one bit set in the result. - static APInt getOneBitSet(unsigned numBits, unsigned BitNo) { - APInt Res(numBits, 0); - Res.setBit(BitNo); - return Res; - } - - /// Get a value with a block of bits set. - /// - /// Constructs an APInt value that has a contiguous range of bits set. The - /// bits from loBit (inclusive) to hiBit (exclusive) will be set. All other - /// bits will be zero. For example, with parameters(32, 0, 16) you would get - /// 0x0000FFFF. Please call getBitsSetWithWrap if \p loBit may be greater than - /// \p hiBit. - /// - /// \param numBits the intended bit width of the result - /// \param loBit the index of the lowest bit set. - /// \param hiBit the index of the highest bit set. - /// - /// \returns An APInt value with the requested bits set. - static APInt getBitsSet(unsigned numBits, unsigned loBit, unsigned hiBit) { - assert(loBit <= hiBit && "loBit greater than hiBit"); - APInt Res(numBits, 0); - Res.setBits(loBit, hiBit); - return Res; - } - - /// Wrap version of getBitsSet. - /// If \p hiBit is bigger than \p loBit, this is same with getBitsSet. - /// If \p hiBit is not bigger than \p loBit, the set bits "wrap". For example, - /// with parameters (32, 28, 4), you would get 0xF000000F. - /// If \p hiBit is equal to \p loBit, you would get a result with all bits - /// set. - static APInt getBitsSetWithWrap(unsigned numBits, unsigned loBit, - unsigned hiBit) { - APInt Res(numBits, 0); - Res.setBitsWithWrap(loBit, hiBit); - return Res; + return (numBits == Ones) && + ((Ones + countLeadingZerosSlowCase()) == BitWidth); } - /// Get a value with upper bits starting at loBit set. - /// - /// Constructs an APInt value that has a contiguous range of bits set. The - /// bits from loBit (inclusive) to numBits (exclusive) will be set. All other - /// bits will be zero. For example, with parameters(32, 12) you would get - /// 0xFFFFF000. - /// - /// \param numBits the intended bit width of the result - /// \param loBit the index of the lowest bit to set. - /// - /// \returns An APInt value with the requested bits set. - static APInt getBitsSetFrom(unsigned numBits, unsigned loBit) { - APInt Res(numBits, 0); - Res.setBitsFrom(loBit); - return Res; + /// \returns true if this APInt is a non-empty sequence of ones starting at + /// the least significant bit with the remainder zero. + /// Ex. isMask(0x0000FFFFU) == true. + bool isMask() const { + if (isSingleWord()) + return isMask_64(U.VAL); + unsigned Ones = countTrailingOnesSlowCase(); + return (Ones > 0) && ((Ones + countLeadingZerosSlowCase()) == BitWidth); } - /// Get a value with high bits set - /// - /// Constructs an APInt value that has the top hiBitsSet bits set. - /// - /// \param numBits the bitwidth of the result - /// \param hiBitsSet the number of high-order bits set in the result. - static APInt getHighBitsSet(unsigned numBits, unsigned hiBitsSet) { - APInt Res(numBits, 0); - Res.setHighBits(hiBitsSet); - return Res; + /// Return true if this APInt value contains a sequence of ones with + /// the remainder zero. + bool isShiftedMask() const { + if (isSingleWord()) + return isShiftedMask_64(U.VAL); + unsigned Ones = countPopulationSlowCase(); + unsigned LeadZ = countLeadingZerosSlowCase(); + return (Ones + LeadZ + countTrailingZeros()) == BitWidth; } - /// Get a value with low bits set + /// Compute an APInt containing numBits highbits from this APInt. /// - /// Constructs an APInt value that has the bottom loBitsSet bits set. + /// Get an APInt with the same BitWidth as this APInt, just zero mask the low + /// bits and right shift to the least significant bit. /// - /// \param numBits the bitwidth of the result - /// \param loBitsSet the number of low-order bits set in the result. - static APInt getLowBitsSet(unsigned numBits, unsigned loBitsSet) { - APInt Res(numBits, 0); - Res.setLowBits(loBitsSet); - return Res; - } + /// \returns the high "numBits" bits of this APInt. + APInt getHiBits(unsigned numBits) const; - /// Return a value containing V broadcasted over NewLen bits. - static APInt getSplat(unsigned NewLen, const APInt &V); + /// Compute an APInt containing numBits lowbits from this APInt. + /// + /// Get an APInt with the same BitWidth as this APInt, just zero mask the high + /// bits. + /// + /// \returns the low "numBits" bits of this APInt. + APInt getLoBits(unsigned numBits) const; /// Determine if two APInts have the same value, after zero-extending /// one of them (if needed!) to ensure that the bit-widths match. @@ -701,12 +540,10 @@ public: /// \name Unary Operators /// @{ - /// Postfix increment operator. - /// - /// Increments *this by 1. + /// Postfix increment operator. Increment *this by 1. /// /// \returns a new APInt value representing the original value of *this. - const APInt operator++(int) { + APInt operator++(int) { APInt API(*this); ++(*this); return API; @@ -717,12 +554,10 @@ public: /// \returns *this incremented by one APInt &operator++(); - /// Postfix decrement operator. - /// - /// Decrements *this by 1. + /// Postfix decrement operator. Decrement *this by 1. /// /// \returns a new APInt value representing the original value of *this. - const APInt operator--(int) { + APInt operator--(int) { APInt API(*this); --(*this); return API; @@ -733,16 +568,9 @@ public: /// \returns *this decremented by one. APInt &operator--(); - /// Logical negation operator. - /// - /// Performs logical negation operation on this APInt. - /// - /// \returns true if *this is zero, false otherwise. - bool operator!() const { - if (isSingleWord()) - return U.VAL == 0; - return countLeadingZerosSlowCase() == BitWidth; - } + /// Logical negation operation on this APInt returns true if zero, like normal + /// integers. + bool operator!() const { return isZero(); } /// @} /// \name Assignment Operators @@ -752,14 +580,15 @@ public: /// /// \returns *this after assignment of RHS. APInt &operator=(const APInt &RHS) { - // If the bitwidths are the same, we can avoid mucking with memory + // The common case (both source or dest being inline) doesn't require + // allocation or deallocation. if (isSingleWord() && RHS.isSingleWord()) { U.VAL = RHS.U.VAL; BitWidth = RHS.BitWidth; - return clearUnusedBits(); + return *this; } - AssignSlowCase(RHS); + assignSlowCase(RHS); return *this; } @@ -780,7 +609,6 @@ public: BitWidth = that.BitWidth; that.BitWidth = 0; - return *this; } @@ -812,7 +640,7 @@ public: if (isSingleWord()) U.VAL &= RHS.U.VAL; else - AndAssignSlowCase(RHS); + andAssignSlowCase(RHS); return *this; } @@ -827,7 +655,7 @@ public: return *this; } U.pVal[0] &= RHS; - memset(U.pVal+1, 0, (getNumWords() - 1) * APINT_WORD_SIZE); + memset(U.pVal + 1, 0, (getNumWords() - 1) * APINT_WORD_SIZE); return *this; } @@ -842,7 +670,7 @@ public: if (isSingleWord()) U.VAL |= RHS.U.VAL; else - OrAssignSlowCase(RHS); + orAssignSlowCase(RHS); return *this; } @@ -871,7 +699,7 @@ public: if (isSingleWord()) U.VAL ^= RHS.U.VAL; else - XorAssignSlowCase(RHS); + xorAssignSlowCase(RHS); return *this; } @@ -1057,6 +885,17 @@ public: /// Rotate right by rotateAmt. APInt rotr(const APInt &rotateAmt) const; + /// Concatenate the bits from "NewLSB" onto the bottom of *this. This is + /// equivalent to: + /// (this->zext(NewWidth) << NewLSB.getBitWidth()) | NewLSB.zext(NewWidth) + APInt concat(const APInt &NewLSB) const { + /// If the result will be small, then both the merged values are small. + unsigned NewWidth = getBitWidth() + NewLSB.getBitWidth(); + if (NewWidth <= APINT_BITS_PER_WORD) + return APInt(NewWidth, (U.VAL << NewLSB.getBitWidth()) | NewLSB.U.VAL); + return concatSlowCase(NewLSB); + } + /// Unsigned division operation. /// /// Perform an unsigned divide operation on this APInt by RHS. Both this and @@ -1151,7 +990,7 @@ public: assert(BitWidth == RHS.BitWidth && "Comparison requires equal bit widths"); if (isSingleWord()) return U.VAL == RHS.U.VAL; - return EqualSlowCase(RHS); + return equalSlowCase(RHS); } /// Equality operator. @@ -1436,8 +1275,6 @@ public: clearUnusedBits(); } - /// Set a given bit to 1. - /// /// Set the given bit to 1 whose position is given as "bitPosition". void setBit(unsigned BitPosition) { assert(BitPosition < BitWidth && "BitPosition out of range"); @@ -1449,9 +1286,7 @@ public: } /// Set the sign bit to 1. - void setSignBit() { - setBit(BitWidth - 1); - } + void setSignBit() { setBit(BitWidth - 1); } /// Set a given bit to a given value. void setBitVal(unsigned BitPosition, bool BitValue) { @@ -1497,14 +1332,10 @@ public: } /// Set the top bits starting from loBit. - void setBitsFrom(unsigned loBit) { - return setBits(loBit, BitWidth); - } + void setBitsFrom(unsigned loBit) { return setBits(loBit, BitWidth); } /// Set the bottom loBits bits. - void setLowBits(unsigned loBits) { - return setBits(0, loBits); - } + void setLowBits(unsigned loBits) { return setBits(0, loBits); } /// Set the top hiBits bits. void setHighBits(unsigned hiBits) { @@ -1539,9 +1370,7 @@ public: } /// Set the sign bit to 0. - void clearSignBit() { - clearBit(BitWidth - 1); - } + void clearSignBit() { clearBit(BitWidth - 1); } /// Toggle every bit to its opposite value. void flipAllBits() { @@ -1629,8 +1458,10 @@ public: /// uint64_t. The bitwidth must be <= 64 or the value must fit within a /// uint64_t. Otherwise an assertion will result. uint64_t getZExtValue() const { - if (isSingleWord()) + if (isSingleWord()) { + assert(BitWidth && "zero width values not allowed"); return U.VAL; + } assert(getActiveBits() <= 64 && "Too many bits for uint64_t"); return U.pVal[0]; } @@ -1678,8 +1509,11 @@ public: /// \returns 0 if the high order bit is not set, otherwise returns the number /// of 1 bits from the most significant to the least unsigned countLeadingOnes() const { - if (isSingleWord()) + if (isSingleWord()) { + if (LLVM_UNLIKELY(BitWidth == 0)) + return 0; return llvm::countLeadingOnes(U.VAL << (APINT_BITS_PER_WORD - BitWidth)); + } return countLeadingOnesSlowCase(); } @@ -1774,9 +1608,7 @@ public: /// The conversion does not do a translation from integer to double, it just /// re-interprets the bits as a double. Note that it is valid to do this on /// any bit width. Exactly 64 bits will be translated. - double bitsToDouble() const { - return BitsToDouble(getWord(0)); - } + double bitsToDouble() const { return BitsToDouble(getWord(0)); } /// Converts APInt bits to a float /// @@ -1808,7 +1640,7 @@ public: /// @{ /// \returns the floor log base 2 of this APInt. - unsigned logBase2() const { return getActiveBits() - 1; } + unsigned logBase2() const { return getActiveBits() - 1; } /// \returns the ceil log base 2 of this APInt. unsigned ceilLogBase2() const { @@ -1826,25 +1658,7 @@ public: /// /// to get around any mathematical concerns resulting from /// referencing 2 in a space where 2 does no exist. - unsigned nearestLogBase2() const { - // Special case when we have a bitwidth of 1. If VAL is 1, then we - // get 0. If VAL is 0, we get WORDTYPE_MAX which gets truncated to - // UINT32_MAX. - if (BitWidth == 1) - return U.VAL - 1; - - // Handle the zero case. - if (isNullValue()) - return UINT32_MAX; - - // The non-zero case is handled by computing: - // - // nearestLogBase2(x) = logBase2(x) + x[logBase2(x)-1]. - // - // where x[i] is referring to the value of the ith bit of x. - unsigned lg = logBase2(); - return lg + unsigned((*this)[lg - 1]); - } + unsigned nearestLogBase2() const; /// \returns the log base 2 of this APInt if its an exact power of two, -1 /// otherwise @@ -1854,12 +1668,12 @@ public: return logBase2(); } - /// Compute the square root + /// Compute the square root. APInt sqrt() const; - /// Get the absolute value; - /// - /// If *this is < 0 then return -(*this), otherwise *this; + /// Get the absolute value. If *this is < 0 then return -(*this), otherwise + /// *this. Note that the "most negative" signed number (e.g. -128 for 8 bit + /// wide APInt) is unchanged due to how negation works. APInt abs() const { if (isNegative()) return -(*this); @@ -1869,18 +1683,6 @@ public: /// \returns the multiplicative inverse for a given modulo. APInt multiplicativeInverse(const APInt &modulo) const; - /// @} - /// \name Support for division by constant - /// @{ - - /// Calculate the magic number for signed division by a constant. - struct ms; - ms magic() const; - - /// Calculate the magic number for unsigned division by a constant. - struct mu; - mu magicu(unsigned LeadingZeros = 0) const; - /// @} /// \name Building-block Operations for APInt and APFloat /// @{ @@ -1908,9 +1710,8 @@ public: /// DST, of dstCOUNT parts, such that the bit srcLSB becomes the least /// significant bit of DST. All high bits above srcBITS in DST are /// zero-filled. - static void tcExtract(WordType *, unsigned dstCount, - const WordType *, unsigned srcBits, - unsigned srcLSB); + static void tcExtract(WordType *, unsigned dstCount, const WordType *, + unsigned srcBits, unsigned srcLSB); /// Set the given bit of a bignum. Zero-based. static void tcSetBit(WordType *, unsigned bit); @@ -1927,14 +1728,13 @@ public: static void tcNegate(WordType *, unsigned); /// DST += RHS + CARRY where CARRY is zero or one. Returns the carry flag. - static WordType tcAdd(WordType *, const WordType *, - WordType carry, unsigned); + static WordType tcAdd(WordType *, const WordType *, WordType carry, unsigned); /// DST += RHS. Returns the carry flag. static WordType tcAddPart(WordType *, WordType, unsigned); /// DST -= RHS + CARRY where CARRY is zero or one. Returns the carry flag. - static WordType tcSubtract(WordType *, const WordType *, - WordType carry, unsigned); + static WordType tcSubtract(WordType *, const WordType *, WordType carry, + unsigned); /// DST -= RHS. Returns the carry flag. static WordType tcSubtractPart(WordType *, WordType, unsigned); @@ -1950,8 +1750,7 @@ public: /// otherwise overflow occurred and return one. static int tcMultiplyPart(WordType *dst, const WordType *src, WordType multiplier, WordType carry, - unsigned srcParts, unsigned dstParts, - bool add); + unsigned srcParts, unsigned dstParts, bool add); /// DST = LHS * RHS, where DST has the same width as the operands and is /// filled with the least significant parts of the result. Returns one if @@ -1962,8 +1761,8 @@ public: /// DST = LHS * RHS, where DST has width the sum of the widths of the /// operands. No overflow occurs. DST must be disjoint from both operands. - static void tcFullMultiply(WordType *, const WordType *, - const WordType *, unsigned, unsigned); + static void tcFullMultiply(WordType *, const WordType *, const WordType *, + unsigned, unsigned); /// If RHS is zero LHS and REMAINDER are left unchanged, return one. /// Otherwise set LHS to LHS / RHS with the fractional part discarded, set @@ -1974,9 +1773,8 @@ public: /// SCRATCH is a bignum of the same size as the operands and result for use by /// the routine; its contents need not be initialized and are destroyed. LHS, /// REMAINDER and SCRATCH must be distinct. - static int tcDivide(WordType *lhs, const WordType *rhs, - WordType *remainder, WordType *scratch, - unsigned parts); + static int tcDivide(WordType *lhs, const WordType *rhs, WordType *remainder, + WordType *scratch, unsigned parts); /// Shift a bignum left Count bits. Shifted in bits are zero. There are no /// restrictions on Count. @@ -1986,12 +1784,6 @@ public: /// restrictions on Count. static void tcShiftRight(WordType *, unsigned Words, unsigned Count); - /// The obvious AND, OR and XOR and complement operations. - static void tcAnd(WordType *, const WordType *, unsigned); - static void tcOr(WordType *, const WordType *, unsigned); - static void tcXor(WordType *, const WordType *, unsigned); - static void tcComplement(WordType *, unsigned); - /// Comparison (unsigned) of two bignums. static int tcCompare(const WordType *, const WordType *, unsigned); @@ -2005,26 +1797,185 @@ public: return tcSubtractPart(dst, 1, parts); } - /// Set the least significant BITS and clear the rest. - static void tcSetLeastSignificantBits(WordType *, unsigned, unsigned bits); + /// Used to insert APInt objects, or objects that contain APInt objects, into + /// FoldingSets. + void Profile(FoldingSetNodeID &id) const; /// debug method void dump() const; - /// @} -}; + /// Returns whether this instance allocated memory. + bool needsCleanup() const { return !isSingleWord(); } -/// Magic data for optimising signed division by a constant. -struct APInt::ms { - APInt m; ///< magic number - unsigned s; ///< shift amount -}; +private: + /// This union is used to store the integer value. When the + /// integer bit-width <= 64, it uses VAL, otherwise it uses pVal. + union { + uint64_t VAL; ///< Used to store the <= 64 bits integer value. + uint64_t *pVal; ///< Used to store the >64 bits integer value. + } U; + + unsigned BitWidth; ///< The number of bits in this APInt. + + friend struct DenseMapInfo; + friend class APSInt; + + /// This constructor is used only internally for speed of construction of + /// temporaries. It is unsafe since it takes ownership of the pointer, so it + /// is not public. + APInt(uint64_t *val, unsigned bits) : BitWidth(bits) { U.pVal = val; } + + /// Determine which word a bit is in. + /// + /// \returns the word position for the specified bit position. + static unsigned whichWord(unsigned bitPosition) { + return bitPosition / APINT_BITS_PER_WORD; + } + + /// Determine which bit in a word the specified bit position is in. + static unsigned whichBit(unsigned bitPosition) { + return bitPosition % APINT_BITS_PER_WORD; + } + + /// Get a single bit mask. + /// + /// \returns a uint64_t with only bit at "whichBit(bitPosition)" set + /// This method generates and returns a uint64_t (word) mask for a single + /// bit at a specific bit position. This is used to mask the bit in the + /// corresponding word. + static uint64_t maskBit(unsigned bitPosition) { + return 1ULL << whichBit(bitPosition); + } + + /// Clear unused high order bits + /// + /// This method is used internally to clear the top "N" bits in the high order + /// word that are not used by the APInt. This is needed after the most + /// significant word is assigned a value to ensure that those bits are + /// zero'd out. + APInt &clearUnusedBits() { + // Compute how many bits are used in the final word. + unsigned WordBits = ((BitWidth - 1) % APINT_BITS_PER_WORD) + 1; + + // Mask out the high bits. + uint64_t mask = WORDTYPE_MAX >> (APINT_BITS_PER_WORD - WordBits); + if (LLVM_UNLIKELY(BitWidth == 0)) + mask = 0; + + if (isSingleWord()) + U.VAL &= mask; + else + U.pVal[getNumWords() - 1] &= mask; + return *this; + } + + /// Get the word corresponding to a bit position + /// \returns the corresponding word for the specified bit position. + uint64_t getWord(unsigned bitPosition) const { + return isSingleWord() ? U.VAL : U.pVal[whichWord(bitPosition)]; + } + + /// Utility method to change the bit width of this APInt to new bit width, + /// allocating and/or deallocating as necessary. There is no guarantee on the + /// value of any bits upon return. Caller should populate the bits after. + void reallocate(unsigned NewBitWidth); + + /// Convert a char array into an APInt + /// + /// \param radix 2, 8, 10, 16, or 36 + /// Converts a string into a number. The string must be non-empty + /// and well-formed as a number of the given base. The bit-width + /// must be sufficient to hold the result. + /// + /// This is used by the constructors that take string arguments. + /// + /// StringRef::getAsInteger is superficially similar but (1) does + /// not assume that the string is well-formed and (2) grows the + /// result to hold the input. + void fromString(unsigned numBits, StringRef str, uint8_t radix); + + /// An internal division function for dividing APInts. + /// + /// This is used by the toString method to divide by the radix. It simply + /// provides a more convenient form of divide for internal use since KnuthDiv + /// has specific constraints on its inputs. If those constraints are not met + /// then it provides a simpler form of divide. + static void divide(const WordType *LHS, unsigned lhsWords, + const WordType *RHS, unsigned rhsWords, WordType *Quotient, + WordType *Remainder); + + /// out-of-line slow case for inline constructor + void initSlowCase(uint64_t val, bool isSigned); + + /// shared code between two array constructors + void initFromArray(ArrayRef array); + + /// out-of-line slow case for inline copy constructor + void initSlowCase(const APInt &that); + + /// out-of-line slow case for shl + void shlSlowCase(unsigned ShiftAmt); + + /// out-of-line slow case for lshr. + void lshrSlowCase(unsigned ShiftAmt); + + /// out-of-line slow case for ashr. + void ashrSlowCase(unsigned ShiftAmt); + + /// out-of-line slow case for operator= + void assignSlowCase(const APInt &RHS); + + /// out-of-line slow case for operator== + bool equalSlowCase(const APInt &RHS) const LLVM_READONLY; + + /// out-of-line slow case for countLeadingZeros + unsigned countLeadingZerosSlowCase() const LLVM_READONLY; + + /// out-of-line slow case for countLeadingOnes. + unsigned countLeadingOnesSlowCase() const LLVM_READONLY; + + /// out-of-line slow case for countTrailingZeros. + unsigned countTrailingZerosSlowCase() const LLVM_READONLY; + + /// out-of-line slow case for countTrailingOnes + unsigned countTrailingOnesSlowCase() const LLVM_READONLY; + + /// out-of-line slow case for countPopulation + unsigned countPopulationSlowCase() const LLVM_READONLY; + + /// out-of-line slow case for intersects. + bool intersectsSlowCase(const APInt &RHS) const LLVM_READONLY; + + /// out-of-line slow case for isSubsetOf. + bool isSubsetOfSlowCase(const APInt &RHS) const LLVM_READONLY; + + /// out-of-line slow case for setBits. + void setBitsSlowCase(unsigned loBit, unsigned hiBit); + + /// out-of-line slow case for flipAllBits. + void flipAllBitsSlowCase(); + + /// out-of-line slow case for concat. + APInt concatSlowCase(const APInt &NewLSB) const; + + /// out-of-line slow case for operator&=. + void andAssignSlowCase(const APInt &RHS); -/// Magic data for optimising unsigned division by a constant. -struct APInt::mu { - APInt m; ///< magic number - bool a; ///< add indicator - unsigned s; ///< shift amount + /// out-of-line slow case for operator|=. + void orAssignSlowCase(const APInt &RHS); + + /// out-of-line slow case for operator^=. + void xorAssignSlowCase(const APInt &RHS); + + /// Unsigned comparison. Returns -1, 0, or 1 if this APInt is less than, equal + /// to, or greater than RHS. + int compare(const APInt &RHS) const LLVM_READONLY; + + /// Signed comparison. Returns -1, 0, or 1 if this APInt is less than, equal + /// to, or greater than RHS. + int compareSigned(const APInt &RHS) const LLVM_READONLY; + + /// @} }; inline bool operator==(uint64_t V1, const APInt &V2) { return V2 == V1; } @@ -2161,7 +2112,6 @@ inline APInt operator*(uint64_t LHS, APInt b) { return b; } - namespace APIntOps { /// Determine the smaller of two APInts considered to be signed. @@ -2277,7 +2227,16 @@ Optional SolveQuadraticEquationWrap(APInt A, APInt B, APInt C, Optional GetMostSignificantDifferentBit(const APInt &A, const APInt &B); -} // End of APIntOps namespace +/// Splat/Merge neighboring bits to widen/narrow the bitmask represented +/// by \param A to \param NewBitWidth bits. +/// +/// e.g. ScaleBitMask(0b0101, 8) -> 0b00110011 +/// e.g. ScaleBitMask(0b00011011, 4) -> 0b0111 +/// A.getBitwidth() or NewBitWidth must be a whole multiples of the other. +/// +/// TODO: Do we need a mode where all bits must be set when merging down? +APInt ScaleBitMask(const APInt &A, unsigned NewBitWidth); +} // namespace APIntOps // See friend declaration above. This additional declaration is required in // order to compile LLVM with IBM xlC compiler. @@ -2292,7 +2251,7 @@ void StoreIntToMemory(const APInt &IntVal, uint8_t *Dst, unsigned StoreBytes); void LoadIntFromMemory(APInt &IntVal, const uint8_t *Src, unsigned LoadBytes); /// Provide DenseMapInfo for APInt. -template <> struct DenseMapInfo { +template <> struct DenseMapInfo { static inline APInt getEmptyKey() { APInt V(nullptr, 0); V.U.VAL = 0; diff --git a/llvm/include/llvm/ADT/APSInt.h b/llvm/include/llvm/ADT/APSInt.h index 1509d472f131..c1cf3c546070 100644 --- a/llvm/include/llvm/ADT/APSInt.h +++ b/llvm/include/llvm/ADT/APSInt.h @@ -58,7 +58,7 @@ public: /// that 0 is not a positive value. /// /// \returns true if this APSInt is positive. - bool isStrictlyPositive() const { return isNonNegative() && !isNullValue(); } + bool isStrictlyPositive() const { return isNonNegative() && !isZero(); } APSInt &operator=(APInt RHS) { // Retain our current sign. @@ -344,17 +344,17 @@ inline raw_ostream &operator<<(raw_ostream &OS, const APSInt &I) { } /// Provide DenseMapInfo for APSInt, using the DenseMapInfo for APInt. -template <> struct DenseMapInfo { +template <> struct DenseMapInfo { static inline APSInt getEmptyKey() { - return APSInt(DenseMapInfo::getEmptyKey()); + return APSInt(DenseMapInfo::getEmptyKey()); } static inline APSInt getTombstoneKey() { - return APSInt(DenseMapInfo::getTombstoneKey()); + return APSInt(DenseMapInfo::getTombstoneKey()); } static unsigned getHashValue(const APSInt &Key) { - return DenseMapInfo::getHashValue(Key); + return DenseMapInfo::getHashValue(Key); } static bool isEqual(const APSInt &LHS, const APSInt &RHS) { diff --git a/llvm/include/llvm/ADT/ArrayRef.h b/llvm/include/llvm/ADT/ArrayRef.h index 2df49223c987..61f85cfc812b 100644 --- a/llvm/include/llvm/ADT/ArrayRef.h +++ b/llvm/include/llvm/ADT/ArrayRef.h @@ -26,8 +26,6 @@ namespace llvm { - template struct DenseMapInfo; - /// ArrayRef - Represent a constant reference to an array (0 or more elements /// consecutively in memory), i.e. a start pointer and a length. It allows /// various APIs to take consecutive elements easily and conveniently. @@ -572,7 +570,7 @@ namespace llvm { } // Provide DenseMapInfo for ArrayRefs. - template struct DenseMapInfo> { + template struct DenseMapInfo, void> { static inline ArrayRef getEmptyKey() { return ArrayRef( reinterpret_cast(~static_cast(0)), size_t(0)); diff --git a/llvm/include/llvm/ADT/BitVector.h b/llvm/include/llvm/ADT/BitVector.h index 31d388073633..cd1964cbdd98 100644 --- a/llvm/include/llvm/ADT/BitVector.h +++ b/llvm/include/llvm/ADT/BitVector.h @@ -85,7 +85,7 @@ class BitVector { unsigned Size; // Size of bitvector in bits. public: - typedef unsigned size_type; + using size_type = unsigned; // Encapsulation of a single bit. class reference { @@ -536,8 +536,8 @@ public: [&Arg](auto const &BV) { return Arg.size() == BV; }) && "consistent sizes"); Out.resize(Arg.size()); - for (size_t i = 0, e = Arg.Bits.size(); i != e; ++i) - Out.Bits[i] = f(Arg.Bits[i], Args.Bits[i]...); + for (size_type I = 0, E = Arg.Bits.size(); I != E; ++I) + Out.Bits[I] = f(Arg.Bits[I], Args.Bits[I]...); Out.clear_unused_bits(); return Out; } @@ -545,16 +545,16 @@ public: BitVector &operator|=(const BitVector &RHS) { if (size() < RHS.size()) resize(RHS.size()); - for (size_t i = 0, e = RHS.Bits.size(); i != e; ++i) - Bits[i] |= RHS.Bits[i]; + for (size_type I = 0, E = RHS.Bits.size(); I != E; ++I) + Bits[I] |= RHS.Bits[I]; return *this; } BitVector &operator^=(const BitVector &RHS) { if (size() < RHS.size()) resize(RHS.size()); - for (size_t i = 0, e = RHS.Bits.size(); i != e; ++i) - Bits[i] ^= RHS.Bits[i]; + for (size_type I = 0, E = RHS.Bits.size(); I != E; ++I) + Bits[I] ^= RHS.Bits[I]; return *this; } @@ -808,11 +808,11 @@ private: public: /// Return the size (in bytes) of the bit vector. - size_t getMemorySize() const { return Bits.size() * sizeof(BitWord); } - size_t getBitCapacity() const { return Bits.size() * BITWORD_SIZE; } + size_type getMemorySize() const { return Bits.size() * sizeof(BitWord); } + size_type getBitCapacity() const { return Bits.size() * BITWORD_SIZE; } }; -inline size_t capacity_in_bytes(const BitVector &X) { +inline BitVector::size_type capacity_in_bytes(const BitVector &X) { return X.getMemorySize(); } @@ -824,8 +824,8 @@ template <> struct DenseMapInfo { return V; } static unsigned getHashValue(const BitVector &V) { - return DenseMapInfo>>::getHashValue( - std::make_pair(V.size(), V.getData())); + return DenseMapInfo>>:: + getHashValue(std::make_pair(V.size(), V.getData())); } static bool isEqual(const BitVector &LHS, const BitVector &RHS) { if (LHS.isInvalid() || RHS.isInvalid()) diff --git a/llvm/include/llvm/ADT/CombinationGenerator.h b/llvm/include/llvm/ADT/CombinationGenerator.h new file mode 100644 index 000000000000..ab6afd555726 --- /dev/null +++ b/llvm/include/llvm/ADT/CombinationGenerator.h @@ -0,0 +1,148 @@ +//===-- llvm/ADT/CombinationGenerator.h ------------------------*- C++ -*--===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Combination generator. +/// +/// Example: given input {{0, 1}, {2}, {3, 4}} it will produce the following +/// combinations: {0, 2, 3}, {0, 2, 4}, {1, 2, 3}, {1, 2, 4}. +/// +/// It is useful to think of input as vector-of-vectors, where the +/// outer vector is the variable space, and inner vector is choice space. +/// The number of choices for each variable can be different. +/// +/// As for implementation, it is useful to think of this as a weird number, +/// where each digit (==variable) may have different base (==number of choices). +/// Thus modelling of 'produce next combination' is exactly analogous to the +/// incrementing of an number - increment lowest digit (pick next choice for the +/// variable), and if it wrapped to the beginning then increment next digit. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_ADT_COMBINATIONGENERATOR_H +#define LLVM_ADT_COMBINATIONGENERATOR_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include +#include + +namespace llvm { + +template +class CombinationGenerator { + template struct WrappingIterator { + using value_type = T; + + const ArrayRef Range; + typename decltype(Range)::const_iterator Position; + + // Rewind the tape, placing the position to again point at the beginning. + void rewind() { Position = Range.begin(); } + + // Advance position forward, possibly wrapping to the beginning. + // Returns whether the wrap happened. + bool advance() { + ++Position; + bool Wrapped = Position == Range.end(); + if (Wrapped) + rewind(); + return Wrapped; + } + + // Get the value at which we are currently pointing. + const value_type &operator*() const { return *Position; } + + WrappingIterator(ArrayRef Range_) : Range(Range_) { + assert(!Range.empty() && "The range must not be empty."); + rewind(); + } + }; + + const ArrayRef VariablesChoices; + + void performGeneration( + const function_ref)> Callback) const { + SmallVector, variable_smallsize> + VariablesState; + + // 'increment' of the the whole VariablesState is defined identically to the + // increment of a number: starting from the least significant element, + // increment it, and if it wrapped, then propagate that carry by also + // incrementing next (more significant) element. + auto IncrementState = + [](MutableArrayRef> VariablesState) + -> bool { + for (WrappingIterator &Variable : + llvm::reverse(VariablesState)) { + bool Wrapped = Variable.advance(); + if (!Wrapped) + return false; // There you go, next combination is ready. + // We have carry - increment more significant variable next.. + } + return true; // MSB variable wrapped, no more unique combinations. + }; + + // Initialize the per-variable state to refer to the possible choices for + // that variable. + VariablesState.reserve(VariablesChoices.size()); + for (ArrayRef VC : VariablesChoices) + VariablesState.emplace_back(VC); + + // Temporary buffer to store each combination before performing Callback. + SmallVector CurrentCombination; + CurrentCombination.resize(VariablesState.size()); + + while (true) { + // Gather the currently-selected variable choices into a vector. + for (auto I : llvm::zip(VariablesState, CurrentCombination)) + std::get<1>(I) = *std::get<0>(I); + // And pass the new combination into callback, as intended. + if (/*Abort=*/Callback(CurrentCombination)) + return; + // And tick the state to next combination, which will be unique. + if (IncrementState(VariablesState)) + return; // All combinations produced. + } + }; + +public: + CombinationGenerator(ArrayRef VariablesChoices_) + : VariablesChoices(VariablesChoices_) { +#ifndef NDEBUG + assert(!VariablesChoices.empty() && "There should be some variables."); + llvm::for_each(VariablesChoices, [](ArrayRef VariableChoices) { + assert(!VariableChoices.empty() && + "There must always be some choice, at least a placeholder one."); + }); +#endif + } + + // How many combinations can we produce, max? + // This is at most how many times the callback will be called. + size_t numCombinations() const { + size_t NumVariants = 1; + for (ArrayRef VariableChoices : VariablesChoices) + NumVariants *= VariableChoices.size(); + assert(NumVariants >= 1 && + "We should always end up producing at least one combination"); + return NumVariants; + } + + // Actually perform exhaustive combination generation. + // Each result will be passed into the callback. + void generate(const function_ref)> Callback) { + performGeneration(Callback); + } +}; + +} // namespace llvm + +#endif diff --git a/llvm/include/llvm/ADT/DenseMapInfo.h b/llvm/include/llvm/ADT/DenseMapInfo.h index d276acbfa6a6..75b7371a3683 100644 --- a/llvm/include/llvm/ADT/DenseMapInfo.h +++ b/llvm/include/llvm/ADT/DenseMapInfo.h @@ -13,10 +13,10 @@ #ifndef LLVM_ADT_DENSEMAPINFO_H #define LLVM_ADT_DENSEMAPINFO_H -#include "llvm/ADT/Hashing.h" #include #include #include +#include #include namespace llvm { @@ -39,7 +39,12 @@ static inline unsigned combineHashValue(unsigned a, unsigned b) { } // end namespace detail -template +/// An information struct used to provide DenseMap with the various necessary +/// components for a given value type `T`. `Enable` is an optional additional +/// parameter that is used to support SFINAE (generally using std::enable_if_t) +/// in derived DenseMapInfo specializations; in non-SFINAE use cases this should +/// just be `void`. +template struct DenseMapInfo { //static inline T getEmptyKey(); //static inline T getTombstoneKey(); @@ -282,13 +287,6 @@ template struct DenseMapInfo> { } }; -template <> struct DenseMapInfo { - static inline hash_code getEmptyKey() { return hash_code(-1); } - static inline hash_code getTombstoneKey() { return hash_code(-2); } - static unsigned getHashValue(hash_code val) { return val; } - static bool isEqual(hash_code LHS, hash_code RHS) { return LHS == RHS; } -}; - } // end namespace llvm #endif // LLVM_ADT_DENSEMAPINFO_H diff --git a/llvm/include/llvm/ADT/EquivalenceClasses.h b/llvm/include/llvm/ADT/EquivalenceClasses.h index 273b00f99d5d..de6bb3bca7e3 100644 --- a/llvm/include/llvm/ADT/EquivalenceClasses.h +++ b/llvm/include/llvm/ADT/EquivalenceClasses.h @@ -30,7 +30,8 @@ namespace llvm { /// /// This implementation is an efficient implementation that only stores one copy /// of the element being indexed per entry in the set, and allows any arbitrary -/// type to be indexed (as long as it can be ordered with operator<). +/// type to be indexed (as long as it can be ordered with operator< or a +/// comparator is provided). /// /// Here is a simple example using integers: /// @@ -54,7 +55,7 @@ namespace llvm { /// 4 /// 5 1 2 /// -template +template > class EquivalenceClasses { /// ECValue - The EquivalenceClasses data structure is just a set of these. /// Each of these represents a relation for a value. First it stores the @@ -101,22 +102,40 @@ class EquivalenceClasses { assert(RHS.isLeader() && RHS.getNext() == nullptr && "Not a singleton!"); } - bool operator<(const ECValue &UFN) const { return Data < UFN.Data; } - bool isLeader() const { return (intptr_t)Next & 1; } const ElemTy &getData() const { return Data; } const ECValue *getNext() const { return (ECValue*)((intptr_t)Next & ~(intptr_t)1); } + }; + + /// A wrapper of the comparator, to be passed to the set. + struct ECValueComparator { + using is_transparent = void; + + ECValueComparator() : compare(Compare()) {} + + bool operator()(const ECValue &lhs, const ECValue &rhs) const { + return compare(lhs.Data, rhs.Data); + } + + template + bool operator()(const T &lhs, const ECValue &rhs) const { + return compare(lhs, rhs.Data); + } + + template + bool operator()(const ECValue &lhs, const T &rhs) const { + return compare(lhs.Data, rhs); + } - template - bool operator<(const T &Val) const { return Data < Val; } + const Compare compare; }; /// TheMapping - This implicitly provides a mapping from ElemTy values to the /// ECValues, it just keeps the key as part of the value. - std::set TheMapping; + std::set TheMapping; public: EquivalenceClasses() = default; diff --git a/llvm/include/llvm/ADT/FunctionExtras.h b/llvm/include/llvm/ADT/FunctionExtras.h index e67ef7377c88..5a37417ddde5 100644 --- a/llvm/include/llvm/ADT/FunctionExtras.h +++ b/llvm/include/llvm/ADT/FunctionExtras.h @@ -37,6 +37,7 @@ #include "llvm/ADT/STLForwardCompat.h" #include "llvm/Support/MemAlloc.h" #include "llvm/Support/type_traits.h" +#include #include #include @@ -64,11 +65,16 @@ template using EnableUnlessSameType = std::enable_if_t, ThisT>::value>; template -using EnableIfCallable = - std::enable_if_t::value || - std::is_convertible()( - std::declval()...)), - Ret>::value>; +using EnableIfCallable = std::enable_if_t, + std::is_same()(std::declval()...)), + Ret>, + std::is_same()( + std::declval()...)), + Ret>, + std::is_convertible()( + std::declval()...)), + Ret>>::value>; template class UniqueFunctionBase { protected: diff --git a/llvm/include/llvm/ADT/Hashing.h b/llvm/include/llvm/ADT/Hashing.h index e296c1c53ebd..74a87a3d8dbb 100644 --- a/llvm/include/llvm/ADT/Hashing.h +++ b/llvm/include/llvm/ADT/Hashing.h @@ -56,6 +56,7 @@ #include namespace llvm { +template struct DenseMapInfo; /// An opaque object representing a hash code. /// @@ -677,6 +678,13 @@ hash_code hash_value(const std::basic_string &arg) { return hash_combine_range(arg.begin(), arg.end()); } +template <> struct DenseMapInfo { + static inline hash_code getEmptyKey() { return hash_code(-1); } + static inline hash_code getTombstoneKey() { return hash_code(-2); } + static unsigned getHashValue(hash_code val) { return val; } + static bool isEqual(hash_code LHS, hash_code RHS) { return LHS == RHS; } +}; + } // namespace llvm #endif diff --git a/llvm/include/llvm/ADT/ImmutableList.h b/llvm/include/llvm/ADT/ImmutableList.h index c9ee494734e7..cf27c5a16d28 100644 --- a/llvm/include/llvm/ADT/ImmutableList.h +++ b/llvm/include/llvm/ADT/ImmutableList.h @@ -220,8 +220,7 @@ public: // Partially-specialized Traits. //===----------------------------------------------------------------------===// -template struct DenseMapInfo; -template struct DenseMapInfo> { +template struct DenseMapInfo, void> { static inline ImmutableList getEmptyKey() { return reinterpret_cast*>(-1); } diff --git a/llvm/include/llvm/ADT/IntervalMap.h b/llvm/include/llvm/ADT/IntervalMap.h index 26a7ed0cd333..3c107a3622a9 100644 --- a/llvm/include/llvm/ADT/IntervalMap.h +++ b/llvm/include/llvm/ADT/IntervalMap.h @@ -1137,7 +1137,7 @@ public: /// overlaps(a, b) - Return true if the intervals in this map overlap with the /// interval [a;b]. - bool overlaps(KeyT a, KeyT b) { + bool overlaps(KeyT a, KeyT b) const { assert(Traits::nonEmpty(a, b)); const_iterator I = find(a); if (!I.valid()) diff --git a/llvm/include/llvm/ADT/MapVector.h b/llvm/include/llvm/ADT/MapVector.h index 1de1124f4ea2..f9540999381a 100644 --- a/llvm/include/llvm/ADT/MapVector.h +++ b/llvm/include/llvm/ADT/MapVector.h @@ -43,6 +43,7 @@ class MapVector { "The mapped_type of the specified Map must be an integral type"); public: + using key_type = KeyT; using value_type = typename VectorType::value_type; using size_type = typename VectorType::size_type; diff --git a/llvm/include/llvm/ADT/PointerIntPair.h b/llvm/include/llvm/ADT/PointerIntPair.h index cb8b202c48b7..393ace6b70fc 100644 --- a/llvm/include/llvm/ADT/PointerIntPair.h +++ b/llvm/include/llvm/ADT/PointerIntPair.h @@ -22,7 +22,7 @@ namespace llvm { -template struct DenseMapInfo; +template struct DenseMapInfo; template struct PointerIntPairInfo; @@ -192,7 +192,7 @@ struct PointerIntPairInfo { // Provide specialization of DenseMapInfo for PointerIntPair. template -struct DenseMapInfo> { +struct DenseMapInfo, void> { using Ty = PointerIntPair; static Ty getEmptyKey() { diff --git a/llvm/include/llvm/ADT/PointerUnion.h b/llvm/include/llvm/ADT/PointerUnion.h index c39691061b72..0874f67db3fe 100644 --- a/llvm/include/llvm/ADT/PointerUnion.h +++ b/llvm/include/llvm/ADT/PointerUnion.h @@ -17,42 +17,13 @@ #include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/Support/PointerLikeTypeTraits.h" +#include #include #include #include namespace llvm { -template struct PointerUnionTypeSelectorReturn { - using Return = T; -}; - -/// Get a type based on whether two types are the same or not. -/// -/// For: -/// -/// \code -/// using Ret = typename PointerUnionTypeSelector::Return; -/// \endcode -/// -/// Ret will be EQ type if T1 is same as T2 or NE type otherwise. -template -struct PointerUnionTypeSelector { - using Return = typename PointerUnionTypeSelectorReturn::Return; -}; - -template -struct PointerUnionTypeSelector { - using Return = typename PointerUnionTypeSelectorReturn::Return; -}; - -template -struct PointerUnionTypeSelectorReturn< - PointerUnionTypeSelector> { - using Return = - typename PointerUnionTypeSelector::Return; -}; - namespace pointer_union_detail { /// Determine the number of bits required to store integers with values < n. /// This is ceil(log2(n)). diff --git a/llvm/include/llvm/ADT/STLExtras.h b/llvm/include/llvm/ADT/STLExtras.h index eb001346b609..48f15b02283a 100644 --- a/llvm/include/llvm/ADT/STLExtras.h +++ b/llvm/include/llvm/ADT/STLExtras.h @@ -272,20 +272,24 @@ template auto drop_begin(T &&RangeOrContainer, size_t N = 1) { // be applied whenever operator* is invoked on the iterator. template ()(*std::declval()))> + typename ReferenceTy = + decltype(std::declval()(*std::declval()))> class mapped_iterator : public iterator_adaptor_base< - mapped_iterator, ItTy, - typename std::iterator_traits::iterator_category, - typename std::remove_reference::type> { + mapped_iterator, ItTy, + typename std::iterator_traits::iterator_category, + std::remove_reference_t, + typename std::iterator_traits::difference_type, + std::remove_reference_t *, ReferenceTy> { public: mapped_iterator(ItTy U, FuncTy F) : mapped_iterator::iterator_adaptor_base(std::move(U)), F(std::move(F)) {} ItTy getCurrent() { return this->I; } - FuncReturnTy operator*() const { return F(*this->I); } + const FuncTy &getFunction() const { return F; } + + ReferenceTy operator*() const { return F(*this->I); } private: FuncTy F; @@ -303,6 +307,32 @@ auto map_range(ContainerTy &&C, FuncTy F) { return make_range(map_iterator(C.begin(), F), map_iterator(C.end(), F)); } +/// A base type of mapped iterator, that is useful for building derived +/// iterators that do not need/want to store the map function (as in +/// mapped_iterator). These iterators must simply provide a `mapElement` method +/// that defines how to map a value of the iterator to the provided reference +/// type. +template +class mapped_iterator_base + : public iterator_adaptor_base< + DerivedT, ItTy, + typename std::iterator_traits::iterator_category, + std::remove_reference_t, + typename std::iterator_traits::difference_type, + std::remove_reference_t *, ReferenceTy> { +public: + using BaseT = mapped_iterator_base; + + mapped_iterator_base(ItTy U) + : mapped_iterator_base::iterator_adaptor_base(std::move(U)) {} + + ItTy getCurrent() { return this->I; } + + ReferenceTy operator*() const { + return static_cast(*this).mapElement(*this->I); + } +}; + /// Helper to determine if type T has a member called rbegin(). template class has_rbegin_impl { using yes = char[1]; @@ -371,12 +401,7 @@ class filter_iterator_base typename std::common_type< IterTag, typename std::iterator_traits< WrappedIteratorT>::iterator_category>::type> { - using BaseT = iterator_adaptor_base< - filter_iterator_base, - WrappedIteratorT, - typename std::common_type< - IterTag, typename std::iterator_traits< - WrappedIteratorT>::iterator_category>::type>; + using BaseT = typename filter_iterator_base::iterator_adaptor_base; protected: WrappedIteratorT End; @@ -411,12 +436,10 @@ template class filter_iterator_impl : public filter_iterator_base { - using BaseT = filter_iterator_base; - public: filter_iterator_impl(WrappedIteratorT Begin, WrappedIteratorT End, PredicateT Pred) - : BaseT(Begin, End, Pred) {} + : filter_iterator_impl::filter_iterator_base(Begin, End, Pred) {} }; /// Specialization of filter_iterator_base for bidirectional iteration. @@ -425,8 +448,8 @@ class filter_iterator_impl : public filter_iterator_base { - using BaseT = filter_iterator_base; + using BaseT = typename filter_iterator_impl::filter_iterator_base; + void findPrevValid() { while (!this->Pred(*this->I)) BaseT::operator--(); @@ -514,9 +537,7 @@ template class early_inc_iterator_impl : public iterator_adaptor_base, WrappedIteratorT, std::input_iterator_tag> { - using BaseT = - iterator_adaptor_base, - WrappedIteratorT, std::input_iterator_tag>; + using BaseT = typename early_inc_iterator_impl::iterator_adaptor_base; using PointerT = typename std::iterator_traits::pointer; @@ -630,12 +651,18 @@ protected: return std::tuple(std::prev(std::get(iterators))...); } + template + bool test_all_equals(const zip_common &other, + std::index_sequence) const { + return all_of(std::initializer_list{std::get(this->iterators) == + std::get(other.iterators)...}, + identity{}); + } + public: zip_common(Iters &&... ts) : iterators(std::forward(ts)...) {} - value_type operator*() { return deref(std::index_sequence_for{}); } - - const value_type operator*() const { + value_type operator*() const { return deref(std::index_sequence_for{}); } @@ -650,6 +677,11 @@ public: iterators = tup_dec(std::index_sequence_for{}); return *reinterpret_cast(this); } + + /// Return true if all the iterator are matching `other`'s iterators. + bool all_equals(zip_common &other) { + return test_all_equals(other, std::index_sequence_for{}); + } }; template @@ -801,8 +833,6 @@ public: : iterators(std::forward(ts.first)...), end_iterators(std::forward(ts.second)...) {} - value_type operator*() { return deref(std::index_sequence_for{}); } - value_type operator*() const { return deref(std::index_sequence_for{}); } @@ -1073,8 +1103,7 @@ template class indexed_accessor_range_base { public: - using RangeBaseT = - indexed_accessor_range_base; + using RangeBaseT = indexed_accessor_range_base; /// An iterator element of this range. class iterator : public indexed_accessor_iterator( - owner, curIndex) {} + : iterator::indexed_accessor_iterator(owner, curIndex) {} /// Allow access to the constructor. friend indexed_accessor_range_base class first_or_second_type { +public: + using type = + typename std::conditional_t::value, FirstTy, + std::remove_reference_t>; +}; +} // end namespace detail + /// Given a container of pairs, return a range over the first elements. template auto make_first_range(ContainerTy &&c) { - return llvm::map_range( - std::forward(c), - [](decltype((*std::begin(c))) elt) -> decltype((elt.first)) { - return elt.first; - }); + using EltTy = decltype((*std::begin(c))); + return llvm::map_range(std::forward(c), + [](EltTy elt) -> typename detail::first_or_second_type< + EltTy, decltype((elt.first))>::type { + return elt.first; + }); } /// Given a container of pairs, return a range over the second elements. template auto make_second_range(ContainerTy &&c) { + using EltTy = decltype((*std::begin(c))); return llvm::map_range( std::forward(c), - [](decltype((*std::begin(c))) elt) -> decltype((elt.second)) { + [](EltTy elt) -> + typename detail::first_or_second_type::type { return elt.second; }); } @@ -1260,7 +1307,7 @@ template auto make_second_range(ContainerTy &&c) { /// compares less than the first component of another std::pair. struct less_first { template bool operator()(const T &lhs, const T &rhs) const { - return lhs.first < rhs.first; + return std::less<>()(lhs.first, rhs.first); } }; @@ -1268,7 +1315,7 @@ struct less_first { /// compares less than the second component of another std::pair. struct less_second { template bool operator()(const T &lhs, const T &rhs) const { - return lhs.second < rhs.second; + return std::less<>()(lhs.second, rhs.second); } }; @@ -1877,8 +1924,7 @@ template struct result_pair { } std::size_t index() const { return Index; } - const value_reference value() const { return *Iter; } - value_reference value() { return *Iter; } + value_reference value() const { return *Iter; } private: std::size_t Index = std::numeric_limits::max(); @@ -1887,11 +1933,8 @@ private: template class enumerator_iter - : public iterator_facade_base< - enumerator_iter, std::forward_iterator_tag, result_pair, - typename std::iterator_traits>::difference_type, - typename std::iterator_traits>::pointer, - typename std::iterator_traits>::reference> { + : public iterator_facade_base, std::forward_iterator_tag, + const result_pair> { using result_type = result_pair; public: @@ -1901,7 +1944,6 @@ public: enumerator_iter(std::size_t Index, IterOfRange Iter) : Result(Index, Iter) {} - result_type &operator*() { return Result; } const result_type &operator*() const { return Result; } enumerator_iter &operator++() { @@ -1986,6 +2028,45 @@ decltype(auto) apply_tuple(F &&f, Tuple &&t) { Indices{}); } +namespace detail { + +template +bool all_of_zip_predicate_first(Predicate &&P, Args &&...args) { + auto z = zip(args...); + auto it = z.begin(); + auto end = z.end(); + while (it != end) { + if (!apply_tuple([&](auto &&...args) { return P(args...); }, *it)) + return false; + ++it; + } + return it.all_equals(end); +} + +// Just an adaptor to switch the order of argument and have the predicate before +// the zipped inputs. +template +bool all_of_zip_predicate_last( + std::tuple argsThenPredicate, + std::index_sequence) { + auto constexpr OutputIndex = + std::tuple_size::value - 1; + return all_of_zip_predicate_first(std::get(argsThenPredicate), + std::get(argsThenPredicate)...); +} + +} // end namespace detail + +/// Compare two zipped ranges using the provided predicate (as last argument). +/// Return true if all elements satisfy the predicate and false otherwise. +// Return false if the zipped iterator aren't all at end (size mismatch). +template +bool all_of_zip(ArgsAndPredicate &&...argsAndPredicate) { + return detail::all_of_zip_predicate_last( + std::forward_as_tuple(argsAndPredicate...), + std::make_index_sequence{}); +} + /// Return true if the sequence [Begin, End) has exactly N items. Runs in O(N) /// time. Not meant for use with random-access iterators. /// Can optionally take a predicate to filter lazily some items. diff --git a/llvm/include/llvm/ADT/Sequence.h b/llvm/include/llvm/ADT/Sequence.h index 3e4bf0932222..fdbf397984d0 100644 --- a/llvm/include/llvm/ADT/Sequence.h +++ b/llvm/include/llvm/ADT/Sequence.h @@ -6,9 +6,74 @@ // //===----------------------------------------------------------------------===// /// \file -/// This routine provides some synthesis utilities to produce sequences of -/// values. The names are intentionally kept very short as they tend to occur -/// in common and widely used contexts. +/// Provides some synthesis utilities to produce sequences of values. The names +/// are intentionally kept very short as they tend to occur in common and +/// widely used contexts. +/// +/// The `seq(A, B)` function produces a sequence of values from `A` to up to +/// (but not including) `B`, i.e., [`A`, `B`), that can be safely iterated over. +/// `seq` supports both integral (e.g., `int`, `char`, `uint32_t`) and enum +/// types. `seq_inclusive(A, B)` produces a sequence of values from `A` to `B`, +/// including `B`. +/// +/// Examples with integral types: +/// ``` +/// for (int x : seq(0, 3)) +/// outs() << x << " "; +/// ``` +/// +/// Prints: `0 1 2 `. +/// +/// ``` +/// for (int x : seq_inclusive(0, 3)) +/// outs() << x << " "; +/// ``` +/// +/// Prints: `0 1 2 3 `. +/// +/// Similar to `seq` and `seq_inclusive`, the `enum_seq` and +/// `enum_seq_inclusive` functions produce sequences of enum values that can be +/// iterated over. +/// To enable iteration with enum types, you need to either mark enums as safe +/// to iterate on by specializing `enum_iteration_traits`, or opt into +/// potentially unsafe iteration at every callsite by passing +/// `force_iteration_on_noniterable_enum`. +/// +/// Examples with enum types: +/// ``` +/// namespace X { +/// enum class MyEnum : unsigned {A = 0, B, C}; +/// } // namespace X +/// +/// template <> struct enum_iteration_traits { +/// static contexpr bool is_iterable = true; +/// }; +/// +/// class MyClass { +/// public: +/// enum Safe { D = 3, E, F }; +/// enum MaybeUnsafe { G = 1, H = 2, I = 4 }; +/// }; +/// +/// template <> struct enum_iteration_traits { +/// static contexpr bool is_iterable = true; +/// }; +/// ``` +/// +/// ``` +/// for (auto v : enum_seq(MyClass::Safe::D, MyClass::Safe::F)) +/// outs() << int(v) << " "; +/// ``` +/// +/// Prints: `3 4 `. +/// +/// ``` +/// for (auto v : enum_seq(MyClass::MaybeUnsafe::H, MyClass::MaybeUnsafe::I, +/// force_iteration_on_noniterable_enum)) +/// outs() << int(v) << " "; +/// ``` +/// +/// Prints: `2 3 `. /// //===----------------------------------------------------------------------===// @@ -18,12 +83,31 @@ #include // assert #include // std::random_access_iterator_tag #include // std::numeric_limits -#include // std::underlying_type, std::is_enum +#include // std::is_integral, std::is_enum, std::underlying_type, + // std::enable_if #include "llvm/Support/MathExtras.h" // AddOverflow / SubOverflow namespace llvm { +// Enum traits that marks enums as safe or unsafe to iterate over. +// By default, enum types are *not* considered safe for iteration. +// To allow iteration for your enum type, provide a specialization with +// `is_iterable` set to `true` in the `llvm` namespace. +// Alternatively, you can pass the `force_iteration_on_noniterable_enum` tag +// to `enum_seq` or `enum_seq_inclusive`. +template struct enum_iteration_traits { + static constexpr bool is_iterable = false; +}; + +struct force_iteration_on_noniterable_enum_t { + explicit force_iteration_on_noniterable_enum_t() = default; +}; + +// TODO: Make this `inline` once we update to C++17 to avoid ORD violations. +constexpr force_iteration_on_noniterable_enum_t + force_iteration_on_noniterable_enum; + namespace detail { // Returns whether a value of type U can be represented with type T. @@ -213,27 +297,81 @@ private: iterator PastEndValue; }; -/// Iterate over an integral/enum type from Begin up to - but not including - -/// End. -/// Note on enum iteration: `seq` will generate each consecutive value, even if -/// no enumerator with that value exists. +/// Iterate over an integral type from Begin up to - but not including - End. /// Note: Begin and End values have to be within [INTMAX_MIN, INTMAX_MAX] for /// forward iteration (resp. [INTMAX_MIN + 1, INTMAX_MAX] for reverse /// iteration). -template auto seq(T Begin, T End) { +template ::value && + !std::is_enum::value>> +auto seq(T Begin, T End) { return iota_range(Begin, End, false); } -/// Iterate over an integral/enum type from Begin to End inclusive. -/// Note on enum iteration: `seq_inclusive` will generate each consecutive -/// value, even if no enumerator with that value exists. +/// Iterate over an integral type from Begin to End inclusive. /// Note: Begin and End values have to be within [INTMAX_MIN, INTMAX_MAX - 1] /// for forward iteration (resp. [INTMAX_MIN + 1, INTMAX_MAX - 1] for reverse /// iteration). -template auto seq_inclusive(T Begin, T End) { +template ::value && + !std::is_enum::value>> +auto seq_inclusive(T Begin, T End) { return iota_range(Begin, End, true); } +/// Iterate over an enum type from Begin up to - but not including - End. +/// Note: `enum_seq` will generate each consecutive value, even if no +/// enumerator with that value exists. +/// Note: Begin and End values have to be within [INTMAX_MIN, INTMAX_MAX] for +/// forward iteration (resp. [INTMAX_MIN + 1, INTMAX_MAX] for reverse +/// iteration). +template ::value>> +auto enum_seq(EnumT Begin, EnumT End) { + static_assert(enum_iteration_traits::is_iterable, + "Enum type is not marked as iterable."); + return iota_range(Begin, End, false); +} + +/// Iterate over an enum type from Begin up to - but not including - End, even +/// when `EnumT` is not marked as safely iterable by `enum_iteration_traits`. +/// Note: `enum_seq` will generate each consecutive value, even if no +/// enumerator with that value exists. +/// Note: Begin and End values have to be within [INTMAX_MIN, INTMAX_MAX] for +/// forward iteration (resp. [INTMAX_MIN + 1, INTMAX_MAX] for reverse +/// iteration). +template ::value>> +auto enum_seq(EnumT Begin, EnumT End, force_iteration_on_noniterable_enum_t) { + return iota_range(Begin, End, false); +} + +/// Iterate over an enum type from Begin to End inclusive. +/// Note: `enum_seq_inclusive` will generate each consecutive value, even if no +/// enumerator with that value exists. +/// Note: Begin and End values have to be within [INTMAX_MIN, INTMAX_MAX - 1] +/// for forward iteration (resp. [INTMAX_MIN + 1, INTMAX_MAX - 1] for reverse +/// iteration). +template ::value>> +auto enum_seq_inclusive(EnumT Begin, EnumT End) { + static_assert(enum_iteration_traits::is_iterable, + "Enum type is not marked as iterable."); + return iota_range(Begin, End, true); +} + +/// Iterate over an enum type from Begin to End inclusive, even when `EnumT` +/// is not marked as safely iterable by `enum_iteration_traits`. +/// Note: `enum_seq_inclusive` will generate each consecutive value, even if no +/// enumerator with that value exists. +/// Note: Begin and End values have to be within [INTMAX_MIN, INTMAX_MAX - 1] +/// for forward iteration (resp. [INTMAX_MIN + 1, INTMAX_MAX - 1] for reverse +/// iteration). +template ::value>> +auto enum_seq_inclusive(EnumT Begin, EnumT End, + force_iteration_on_noniterable_enum_t) { + return iota_range(Begin, End, true); +} + } // end namespace llvm #endif // LLVM_ADT_SEQUENCE_H diff --git a/llvm/include/llvm/ADT/SetOperations.h b/llvm/include/llvm/ADT/SetOperations.h index 62f1d26dc1c2..3e30b6bb83d3 100644 --- a/llvm/include/llvm/ADT/SetOperations.h +++ b/llvm/include/llvm/ADT/SetOperations.h @@ -77,15 +77,6 @@ bool set_is_subset(const S1Ty &S1, const S2Ty &S2) { return true; } -/// set_is_strict_subset(A, B) - Return true iff A in B and and A != B -/// -template -bool set_is_strict_subset(const S1Ty &S1, const S2Ty &S2) { - if (S1.size() >= S2.size()) - return false; - return set_is_subset(S1, S2); -} - } // End llvm namespace #endif diff --git a/llvm/include/llvm/ADT/SmallBitVector.h b/llvm/include/llvm/ADT/SmallBitVector.h index f570bac23ad5..51ee5dbbce05 100644 --- a/llvm/include/llvm/ADT/SmallBitVector.h +++ b/llvm/include/llvm/ADT/SmallBitVector.h @@ -60,7 +60,7 @@ class SmallBitVector { "Unsupported word size"); public: - using size_type = unsigned; + using size_type = uintptr_t; // Encapsulation of a single bit. class reference { @@ -96,7 +96,7 @@ private: return reinterpret_cast(X); } - void switchToSmall(uintptr_t NewSmallBits, size_t NewSize) { + void switchToSmall(uintptr_t NewSmallBits, size_type NewSize) { X = 1; setSmallSize(NewSize); setSmallBits(NewSmallBits); @@ -120,9 +120,11 @@ private: } // Return the size. - size_t getSmallSize() const { return getSmallRawBits() >> SmallNumDataBits; } + size_type getSmallSize() const { + return getSmallRawBits() >> SmallNumDataBits; + } - void setSmallSize(size_t Size) { + void setSmallSize(size_type Size) { setSmallRawBits(getSmallBits() | (Size << SmallNumDataBits)); } @@ -189,7 +191,7 @@ public: } /// Returns the number of bits in this bitvector. - size_t size() const { + size_type size() const { return isSmall() ? getSmallSize() : getPointer()->size(); } @@ -336,8 +338,8 @@ public: } else { BitVector *BV = new BitVector(N, t); uintptr_t OldBits = getSmallBits(); - for (size_t i = 0, e = getSmallSize(); i != e; ++i) - (*BV)[i] = (OldBits >> i) & 1; + for (size_type I = 0, E = getSmallSize(); I != E; ++I) + (*BV)[I] = (OldBits >> I) & 1; switchToLarge(BV); } } @@ -346,11 +348,11 @@ public: if (isSmall()) { if (N > SmallNumDataBits) { uintptr_t OldBits = getSmallRawBits(); - size_t SmallSize = getSmallSize(); + size_type SmallSize = getSmallSize(); BitVector *BV = new BitVector(SmallSize); - for (size_t i = 0; i < SmallSize; ++i) - if ((OldBits >> i) & 1) - BV->set(i); + for (size_type I = 0; I < SmallSize; ++I) + if ((OldBits >> I) & 1) + BV->set(I); BV->reserve(N); switchToLarge(BV); } @@ -491,8 +493,8 @@ public: else if (!isSmall() && !RHS.isSmall()) return *getPointer() == *RHS.getPointer(); else { - for (size_t i = 0, e = size(); i != e; ++i) { - if ((*this)[i] != RHS[i]) + for (size_type I = 0, E = size(); I != E; ++I) { + if ((*this)[I] != RHS[I]) return false; } return true; @@ -512,11 +514,11 @@ public: else if (!isSmall() && !RHS.isSmall()) getPointer()->operator&=(*RHS.getPointer()); else { - size_t i, e; - for (i = 0, e = std::min(size(), RHS.size()); i != e; ++i) - (*this)[i] = test(i) && RHS.test(i); - for (e = size(); i != e; ++i) - reset(i); + size_type I, E; + for (I = 0, E = std::min(size(), RHS.size()); I != E; ++I) + (*this)[I] = test(I) && RHS.test(I); + for (E = size(); I != E; ++I) + reset(I); } return *this; } @@ -561,8 +563,8 @@ public: else if (!isSmall() && !RHS.isSmall()) getPointer()->operator|=(*RHS.getPointer()); else { - for (size_t i = 0, e = RHS.size(); i != e; ++i) - (*this)[i] = test(i) || RHS.test(i); + for (size_type I = 0, E = RHS.size(); I != E; ++I) + (*this)[I] = test(I) || RHS.test(I); } return *this; } @@ -574,8 +576,8 @@ public: else if (!isSmall() && !RHS.isSmall()) getPointer()->operator^=(*RHS.getPointer()); else { - for (size_t i = 0, e = RHS.size(); i != e; ++i) - (*this)[i] = test(i) != RHS.test(i); + for (size_type I = 0, E = RHS.size(); I != E; ++I) + (*this)[I] = test(I) != RHS.test(I); } return *this; } @@ -721,8 +723,9 @@ template <> struct DenseMapInfo { } static unsigned getHashValue(const SmallBitVector &V) { uintptr_t Store; - return DenseMapInfo>>::getHashValue( - std::make_pair(V.size(), V.getData(Store))); + return DenseMapInfo< + std::pair>>:: + getHashValue(std::make_pair(V.size(), V.getData(Store))); } static bool isEqual(const SmallBitVector &LHS, const SmallBitVector &RHS) { if (LHS.isInvalid() || RHS.isInvalid()) diff --git a/llvm/include/llvm/ADT/SmallVector.h b/llvm/include/llvm/ADT/SmallVector.h index b8a11030fc33..0d13524f25ce 100644 --- a/llvm/include/llvm/ADT/SmallVector.h +++ b/llvm/include/llvm/ADT/SmallVector.h @@ -1239,13 +1239,22 @@ inline size_t capacity_in_bytes(const SmallVector &X) { return X.capacity_in_bytes(); } +template +using ValueTypeFromRangeType = + typename std::remove_const()))>::type>::type; + /// Given a range of type R, iterate the entire range and return a /// SmallVector with elements of the vector. This is useful, for example, /// when you want to iterate a range and then sort the results. template -SmallVector()))>::type>::type, - Size> +SmallVector, Size> to_vector(R &&Range) { + return {std::begin(Range), std::end(Range)}; +} +template +SmallVector, + CalculateSmallVectorDefaultInlinedElements< + ValueTypeFromRangeType>::value> to_vector(R &&Range) { return {std::begin(Range), std::end(Range)}; } diff --git a/llvm/include/llvm/ADT/StringExtras.h b/llvm/include/llvm/ADT/StringExtras.h index 6bda25b85313..2ca672e7855b 100644 --- a/llvm/include/llvm/ADT/StringExtras.h +++ b/llvm/include/llvm/ADT/StringExtras.h @@ -67,22 +67,27 @@ inline ArrayRef arrayRefFromStringRef(StringRef Input) { /// /// If \p C is not a valid hex digit, -1U is returned. inline unsigned hexDigitValue(char C) { - struct HexTable { - unsigned LUT[255] = {}; - constexpr HexTable() { - // Default initialize everything to invalid. - for (int i = 0; i < 255; ++i) - LUT[i] = ~0U; - // Initialize `0`-`9`. - for (int i = 0; i < 10; ++i) - LUT['0' + i] = i; - // Initialize `A`-`F` and `a`-`f`. - for (int i = 0; i < 6; ++i) - LUT['A' + i] = LUT['a' + i] = 10 + i; - } + /* clang-format off */ + static const int16_t LUT[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // '0'..'9' + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 'A'..'F' + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 'a'..'f' + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, }; - constexpr HexTable Table; - return Table.LUT[static_cast(C)]; + /* clang-format on */ + return LUT[static_cast(C)]; } /// Checks if character \p C is one of the 10 decimal digits. @@ -210,24 +215,31 @@ inline bool tryGetFromHex(StringRef Input, std::string &Output) { if (Input.empty()) return true; - Output.reserve((Input.size() + 1) / 2); + // If the input string is not properly aligned on 2 nibbles we pad out the + // front with a 0 prefix; e.g. `ABC` -> `0ABC`. + Output.resize((Input.size() + 1) / 2); + char *OutputPtr = const_cast(Output.data()); if (Input.size() % 2 == 1) { uint8_t Hex = 0; if (!tryGetHexFromNibbles('0', Input.front(), Hex)) return false; - - Output.push_back(Hex); + *OutputPtr++ = Hex; Input = Input.drop_front(); } - assert(Input.size() % 2 == 0); - while (!Input.empty()) { + // Convert the nibble pairs (e.g. `9C`) into bytes (0x9C). + // With the padding above we know the input is aligned and the output expects + // exactly half as many bytes as nibbles in the input. + size_t InputSize = Input.size(); + assert(InputSize % 2 == 0); + const char *InputPtr = Input.data(); + for (size_t OutputIndex = 0; OutputIndex < InputSize / 2; ++OutputIndex) { uint8_t Hex = 0; - if (!tryGetHexFromNibbles(Input[0], Input[1], Hex)) + if (!tryGetHexFromNibbles(InputPtr[OutputIndex * 2 + 0], // MSB + InputPtr[OutputIndex * 2 + 1], // LSB + Hex)) return false; - - Output.push_back(Hex); - Input = Input.drop_front(2); + OutputPtr[OutputIndex] = Hex; } return true; } @@ -501,6 +513,83 @@ public: } }; +/// A forward iterator over partitions of string over a separator. +class SplittingIterator + : public iterator_facade_base { + char SeparatorStorage; + StringRef Current; + StringRef Next; + StringRef Separator; + +public: + SplittingIterator(StringRef Str, StringRef Separator) + : Next(Str), Separator(Separator) { + ++*this; + } + + SplittingIterator(StringRef Str, char Separator) + : SeparatorStorage(Separator), Next(Str), + Separator(&SeparatorStorage, 1) { + ++*this; + } + + SplittingIterator(const SplittingIterator &R) + : SeparatorStorage(R.SeparatorStorage), Current(R.Current), Next(R.Next), + Separator(R.Separator) { + if (R.Separator.data() == &R.SeparatorStorage) + Separator = StringRef(&SeparatorStorage, 1); + } + + SplittingIterator &operator=(const SplittingIterator &R) { + if (this == &R) + return *this; + + SeparatorStorage = R.SeparatorStorage; + Current = R.Current; + Next = R.Next; + Separator = R.Separator; + if (R.Separator.data() == &R.SeparatorStorage) + Separator = StringRef(&SeparatorStorage, 1); + return *this; + } + + bool operator==(const SplittingIterator &R) const { + assert(Separator == R.Separator); + return Current.data() == R.Current.data(); + } + + const StringRef &operator*() const { return Current; } + + StringRef &operator*() { return Current; } + + SplittingIterator &operator++() { + std::tie(Current, Next) = Next.split(Separator); + return *this; + } +}; + +/// Split the specified string over a separator and return a range-compatible +/// iterable over its partitions. Used to permit conveniently iterating +/// over separated strings like so: +/// +/// \code +/// for (StringRef x : llvm::split("foo,bar,baz", ",")) +/// ...; +/// \end +/// +/// Note that the passed string must remain valid throuhgout lifetime +/// of the iterators. +inline iterator_range split(StringRef Str, StringRef Separator) { + return {SplittingIterator(Str, Separator), + SplittingIterator(StringRef(), Separator)}; +} + +inline iterator_range split(StringRef Str, char Separator) { + return {SplittingIterator(Str, Separator), + SplittingIterator(StringRef(), Separator)}; +} + } // end namespace llvm #endif // LLVM_ADT_STRINGEXTRAS_H diff --git a/llvm/include/llvm/ADT/StringMap.h b/llvm/include/llvm/ADT/StringMap.h index a82afc9a817c..669956d41e0c 100644 --- a/llvm/include/llvm/ADT/StringMap.h +++ b/llvm/include/llvm/ADT/StringMap.h @@ -126,9 +126,7 @@ public: StringMap(std::initializer_list> List) : StringMapImpl(List.size(), static_cast(sizeof(MapEntryTy))) { - for (const auto &P : List) { - insert(P); - } + insert(List); } StringMap(StringMap &&RHS) @@ -297,6 +295,21 @@ public: return try_emplace(KV.first, std::move(KV.second)); } + /// Inserts elements from range [first, last). If multiple elements in the + /// range have keys that compare equivalent, it is unspecified which element + /// is inserted . + template void insert(InputIt First, InputIt Last) { + for (InputIt It = First; It != Last; ++It) + insert(*It); + } + + /// Inserts elements from initializer list ilist. If multiple elements in + /// the range have keys that compare equivalent, it is unspecified which + /// element is inserted + void insert(std::initializer_list> List) { + insert(List.begin(), List.end()); + } + /// Inserts an element or assigns to the current element if the key already /// exists. The return type is the same as try_emplace. template @@ -465,13 +478,7 @@ public: explicit StringMapKeyIterator(StringMapConstIterator Iter) : base(std::move(Iter)) {} - StringRef &operator*() { - Key = this->wrapped()->getKey(); - return Key; - } - -private: - StringRef Key; + StringRef operator*() const { return this->wrapped()->getKey(); } }; } // end namespace llvm diff --git a/llvm/include/llvm/ADT/StringRef.h b/llvm/include/llvm/ADT/StringRef.h index 17e64f7f81bb..9f4b89218042 100644 --- a/llvm/include/llvm/ADT/StringRef.h +++ b/llvm/include/llvm/ADT/StringRef.h @@ -35,7 +35,6 @@ namespace llvm { class APInt; class hash_code; template class SmallVectorImpl; - template struct DenseMapInfo; class StringRef; /// Helper functions for StringRef::getAsInteger. @@ -949,7 +948,7 @@ namespace llvm { hash_code hash_value(StringRef S); // Provide DenseMapInfo for StringRefs. - template <> struct DenseMapInfo { + template <> struct DenseMapInfo { static inline StringRef getEmptyKey() { return StringRef( reinterpret_cast(~static_cast(0)), 0); diff --git a/llvm/include/llvm/ADT/Triple.h b/llvm/include/llvm/ADT/Triple.h index 76f3514050f0..2fd3047acbfd 100644 --- a/llvm/include/llvm/ADT/Triple.h +++ b/llvm/include/llvm/ADT/Triple.h @@ -93,6 +93,8 @@ public: hsail64, // AMD HSAIL with 64-bit pointers spir, // SPIR: standard portable IR for OpenCL 32-bit version spir64, // SPIR: standard portable IR for OpenCL 64-bit version + spirv32, // SPIR-V with 32-bit pointers + spirv64, // SPIR-V with 64-bit pointers kalimba, // Kalimba: generic kalimba shave, // SHAVE: Movidius vector VLIW processors lanai, // Lanai: Lanai 32-bit @@ -106,6 +108,9 @@ public: enum SubArchType { NoSubArch, + ARMSubArch_v9_2a, + ARMSubArch_v9_1a, + ARMSubArch_v9, ARMSubArch_v8_7a, ARMSubArch_v8_6a, ARMSubArch_v8_5a, @@ -290,10 +295,10 @@ public: /// @name Normalization /// @{ - /// normalize - Turn an arbitrary machine specification into the canonical - /// triple form (or something sensible that the Triple class understands if - /// nothing better can reasonably be done). In particular, it handles the - /// common case in which otherwise valid components are in the wrong order. + /// Turn an arbitrary machine specification into the canonical triple form (or + /// something sensible that the Triple class understands if nothing better can + /// reasonably be done). In particular, it handles the common case in which + /// otherwise valid components are in the wrong order. static std::string normalize(StringRef Str); /// Return the normalized form of this triple's string. @@ -303,25 +308,24 @@ public: /// @name Typed Component Access /// @{ - /// getArch - Get the parsed architecture type of this triple. + /// Get the parsed architecture type of this triple. ArchType getArch() const { return Arch; } - /// getSubArch - get the parsed subarchitecture type for this triple. + /// get the parsed subarchitecture type for this triple. SubArchType getSubArch() const { return SubArch; } - /// getVendor - Get the parsed vendor type of this triple. + /// Get the parsed vendor type of this triple. VendorType getVendor() const { return Vendor; } - /// getOS - Get the parsed operating system type of this triple. + /// Get the parsed operating system type of this triple. OSType getOS() const { return OS; } - /// hasEnvironment - Does this triple have the optional environment - /// (fourth) component? + /// Does this triple have the optional environment (fourth) component? bool hasEnvironment() const { return getEnvironmentName() != ""; } - /// getEnvironment - Get the parsed environment type of this triple. + /// Get the parsed environment type of this triple. EnvironmentType getEnvironment() const { return Environment; } /// Parse the version number from the OS name component of the @@ -333,39 +337,39 @@ public: void getEnvironmentVersion(unsigned &Major, unsigned &Minor, unsigned &Micro) const; - /// getFormat - Get the object format for this triple. + /// Get the object format for this triple. ObjectFormatType getObjectFormat() const { return ObjectFormat; } - /// getOSVersion - Parse the version number from the OS name component of the - /// triple, if present. + /// Parse the version number from the OS name component of the triple, if + /// present. /// /// For example, "fooos1.2.3" would return (1, 2, 3). /// /// If an entry is not defined, it will be returned as 0. void getOSVersion(unsigned &Major, unsigned &Minor, unsigned &Micro) const; - /// getOSMajorVersion - Return just the major version number, this is - /// specialized because it is a common query. + /// Return just the major version number, this is specialized because it is a + /// common query. unsigned getOSMajorVersion() const { unsigned Maj, Min, Micro; getOSVersion(Maj, Min, Micro); return Maj; } - /// getMacOSXVersion - Parse the version number as with getOSVersion and then - /// translate generic "darwin" versions to the corresponding OS X versions. - /// This may also be called with IOS triples but the OS X version number is - /// just set to a constant 10.4.0 in that case. Returns true if successful. + /// Parse the version number as with getOSVersion and then translate generic + /// "darwin" versions to the corresponding OS X versions. This may also be + /// called with IOS triples but the OS X version number is just set to a + /// constant 10.4.0 in that case. Returns true if successful. bool getMacOSXVersion(unsigned &Major, unsigned &Minor, unsigned &Micro) const; - /// getiOSVersion - Parse the version number as with getOSVersion. This should - /// only be called with IOS or generic triples. + /// Parse the version number as with getOSVersion. This should only be called + /// with IOS or generic triples. void getiOSVersion(unsigned &Major, unsigned &Minor, unsigned &Micro) const; - /// getWatchOSVersion - Parse the version number as with getOSVersion. This - /// should only be called with WatchOS or generic triples. + /// Parse the version number as with getOSVersion. This should only be called + /// with WatchOS or generic triples. void getWatchOSVersion(unsigned &Major, unsigned &Minor, unsigned &Micro) const; @@ -377,24 +381,24 @@ public: const std::string &getTriple() const { return Data; } - /// getArchName - Get the architecture (first) component of the - /// triple. + /// Get the architecture (first) component of the triple. StringRef getArchName() const; - /// getVendorName - Get the vendor (second) component of the triple. + /// Get the architecture name based on Kind and SubArch. + StringRef getArchName(ArchType Kind, SubArchType SubArch = NoSubArch) const; + + /// Get the vendor (second) component of the triple. StringRef getVendorName() const; - /// getOSName - Get the operating system (third) component of the - /// triple. + /// Get the operating system (third) component of the triple. StringRef getOSName() const; - /// getEnvironmentName - Get the optional environment (fourth) - /// component of the triple, or "" if empty. + /// Get the optional environment (fourth) component of the triple, or "" if + /// empty. StringRef getEnvironmentName() const; - /// getOSAndEnvironmentName - Get the operating system and optional - /// environment components as a single string (separated by a '-' - /// if the environment component is present). + /// Get the operating system and optional environment components as a single + /// string (separated by a '-' if the environment component is present). StringRef getOSAndEnvironmentName() const; /// @} @@ -420,8 +424,8 @@ public: /// Note that this tests for 16-bit pointer width, and nothing else. bool isArch16Bit() const; - /// isOSVersionLT - Helper function for doing comparisons against version - /// numbers included in the target triple. + /// Helper function for doing comparisons against version numbers included in + /// the target triple. bool isOSVersionLT(unsigned Major, unsigned Minor = 0, unsigned Micro = 0) const { unsigned LHS[3]; @@ -443,14 +447,13 @@ public: return isOSVersionLT(RHS[0], RHS[1], RHS[2]); } - /// isMacOSXVersionLT - Comparison function for checking OS X version - /// compatibility, which handles supporting skewed version numbering schemes - /// used by the "darwin" triples. + /// Comparison function for checking OS X version compatibility, which handles + /// supporting skewed version numbering schemes used by the "darwin" triples. bool isMacOSXVersionLT(unsigned Major, unsigned Minor = 0, unsigned Micro = 0) const; - /// isMacOSX - Is this a Mac OS X triple. For legacy reasons, we support both - /// "darwin" and "osx" as OS X triples. + /// Is this a Mac OS X triple. For legacy reasons, we support both "darwin" + /// and "osx" as OS X triples. bool isMacOSX() const { return getOS() == Triple::Darwin || getOS() == Triple::MacOSX; } @@ -480,7 +483,7 @@ public: bool isOSzOS() const { return getOS() == Triple::ZOS; } - /// isOSDarwin - Is this a "Darwin" OS (macOS, iOS, tvOS or watchOS). + /// Is this a "Darwin" OS (macOS, iOS, tvOS or watchOS). bool isOSDarwin() const { return isMacOSX() || isiOS() || isWatchOS(); } @@ -698,6 +701,11 @@ public: return getArch() == Triple::spir || getArch() == Triple::spir64; } + /// Tests whether the target is SPIR-V (32/64-bit). + bool isSPIRV() const { + return getArch() == Triple::spirv32 || getArch() == Triple::spirv64; + } + /// Tests whether the target is NVPTX (32- or 64-bit). bool isNVPTX() const { return getArch() == Triple::nvptx || getArch() == Triple::nvptx64; @@ -720,6 +728,19 @@ public: return getArch() == Triple::arm || getArch() == Triple::armeb; } + /// Tests whether the target supports the EHABI exception + /// handling standard. + bool isTargetEHABICompatible() const { + return (isARM() || isThumb()) && + (getEnvironment() == Triple::EABI || + getEnvironment() == Triple::GNUEABI || + getEnvironment() == Triple::MuslEABI || + getEnvironment() == Triple::EABIHF || + getEnvironment() == Triple::GNUEABIHF || + getEnvironment() == Triple::MuslEABIHF || isAndroid()) && + isOSBinFormatELF(); + } + /// Tests whether the target is AArch64 (little and big endian). bool isAArch64() const { return getArch() == Triple::aarch64 || getArch() == Triple::aarch64_be || @@ -833,46 +854,38 @@ public: /// @name Mutators /// @{ - /// setArch - Set the architecture (first) component of the triple - /// to a known type. - void setArch(ArchType Kind); + /// Set the architecture (first) component of the triple to a known type. + void setArch(ArchType Kind, SubArchType SubArch = NoSubArch); - /// setVendor - Set the vendor (second) component of the triple to a - /// known type. + /// Set the vendor (second) component of the triple to a known type. void setVendor(VendorType Kind); - /// setOS - Set the operating system (third) component of the triple - /// to a known type. + /// Set the operating system (third) component of the triple to a known type. void setOS(OSType Kind); - /// setEnvironment - Set the environment (fourth) component of the triple - /// to a known type. + /// Set the environment (fourth) component of the triple to a known type. void setEnvironment(EnvironmentType Kind); - /// setObjectFormat - Set the object file format + /// Set the object file format. void setObjectFormat(ObjectFormatType Kind); - /// setTriple - Set all components to the new triple \p Str. + /// Set all components to the new triple \p Str. void setTriple(const Twine &Str); - /// setArchName - Set the architecture (first) component of the - /// triple by name. + /// Set the architecture (first) component of the triple by name. void setArchName(StringRef Str); - /// setVendorName - Set the vendor (second) component of the triple - /// by name. + /// Set the vendor (second) component of the triple by name. void setVendorName(StringRef Str); - /// setOSName - Set the operating system (third) component of the - /// triple by name. + /// Set the operating system (third) component of the triple by name. void setOSName(StringRef Str); - /// setEnvironmentName - Set the optional environment (fourth) - /// component of the triple by name. + /// Set the optional environment (fourth) component of the triple by name. void setEnvironmentName(StringRef Str); - /// setOSAndEnvironmentName - Set the operating system and optional - /// environment components with a single string. + /// Set the operating system and optional environment components with a single + /// string. void setOSAndEnvironmentName(StringRef Str); /// @} @@ -938,33 +951,30 @@ public: /// @name Static helpers for IDs. /// @{ - /// getArchTypeName - Get the canonical name for the \p Kind architecture. + /// Get the canonical name for the \p Kind architecture. static StringRef getArchTypeName(ArchType Kind); - /// getArchTypePrefix - Get the "prefix" canonical name for the \p Kind - /// architecture. This is the prefix used by the architecture specific - /// builtins, and is suitable for passing to \see - /// Intrinsic::getIntrinsicForGCCBuiltin(). + /// Get the "prefix" canonical name for the \p Kind architecture. This is the + /// prefix used by the architecture specific builtins, and is suitable for + /// passing to \see Intrinsic::getIntrinsicForGCCBuiltin(). /// /// \return - The architecture prefix, or 0 if none is defined. static StringRef getArchTypePrefix(ArchType Kind); - /// getVendorTypeName - Get the canonical name for the \p Kind vendor. + /// Get the canonical name for the \p Kind vendor. static StringRef getVendorTypeName(VendorType Kind); - /// getOSTypeName - Get the canonical name for the \p Kind operating system. + /// Get the canonical name for the \p Kind operating system. static StringRef getOSTypeName(OSType Kind); - /// getEnvironmentTypeName - Get the canonical name for the \p Kind - /// environment. + /// Get the canonical name for the \p Kind environment. static StringRef getEnvironmentTypeName(EnvironmentType Kind); /// @} /// @name Static helpers for converting alternate architecture names. /// @{ - /// getArchTypeForLLVMName - The canonical type for the given LLVM - /// architecture name (e.g., "x86"). + /// The canonical type for the given LLVM architecture name (e.g., "x86"). static ArchType getArchTypeForLLVMName(StringRef Str); /// @} diff --git a/llvm/include/llvm/ADT/TypeSwitch.h b/llvm/include/llvm/ADT/TypeSwitch.h index 815b9a40afaf..3b7598f3251d 100644 --- a/llvm/include/llvm/ADT/TypeSwitch.h +++ b/llvm/include/llvm/ADT/TypeSwitch.h @@ -35,7 +35,12 @@ public: /// Invoke a case on the derived class with multiple case types. template - DerivedT &Case(CallableT &&caseFn) { + // This is marked always_inline and nodebug so it doesn't show up in stack + // traces at -O0 (or other optimization levels). Large TypeSwitch's are + // common, are equivalent to a switch, and don't add any value to stack + // traces. + LLVM_ATTRIBUTE_ALWAYS_INLINE LLVM_ATTRIBUTE_NODEBUG DerivedT & + Case(CallableT &&caseFn) { DerivedT &derived = static_cast(*this); return derived.template Case(caseFn) .template Case(caseFn); diff --git a/llvm/include/llvm/ADT/iterator.h b/llvm/include/llvm/ADT/iterator.h index b3c6608e9b6e..6f0c42fe08be 100644 --- a/llvm/include/llvm/ADT/iterator.h +++ b/llvm/include/llvm/ADT/iterator.h @@ -35,6 +35,21 @@ namespace llvm { /// terms of addition of one. These aren't equivalent for all iterator /// categories, and respecting that adds a lot of complexity for little gain. /// +/// Iterators are expected to have const rules analogous to pointers, with a +/// single, const-qualified operator*() that returns ReferenceT. This matches +/// the second and third pointers in the following example: +/// \code +/// int Value; +/// { int *I = &Value; } // ReferenceT 'int&' +/// { int *const I = &Value; } // ReferenceT 'int&'; const +/// { const int *I = &Value; } // ReferenceT 'const int&' +/// { const int *const I = &Value; } // ReferenceT 'const int&'; const +/// \endcode +/// If an iterator facade returns a handle to its own state, then T (and +/// PointerT and ReferenceT) should usually be const-qualified. Otherwise, if +/// clients are expected to modify the handle itself, the field can be declared +/// mutable or use const_cast. +/// /// Classes wishing to use `iterator_facade_base` should implement the following /// methods: /// @@ -42,8 +57,7 @@ namespace llvm { /// (All of the following methods) /// - DerivedT &operator=(const DerivedT &R); /// - bool operator==(const DerivedT &R) const; -/// - const T &operator*() const; -/// - T &operator*(); +/// - T &operator*() const; /// - DerivedT &operator++(); /// /// Bidirectional Iterators: @@ -95,6 +109,22 @@ protected: operator ReferenceT() const { return *I; } }; + /// A proxy object for computing a pointer via indirecting a copy of a + /// reference. This is used in APIs which need to produce a pointer but for + /// which the reference might be a temporary. The proxy preserves the + /// reference internally and exposes the pointer via a arrow operator. + class PointerProxy { + friend iterator_facade_base; + + ReferenceT R; + + template + PointerProxy(RefT &&R) : R(std::forward(R)) {} + + public: + PointerT operator->() const { return &R; } + }; + public: DerivedT operator+(DifferenceTypeT n) const { static_assert(std::is_base_of::value, @@ -172,19 +202,13 @@ public: return !(static_cast(*this) < RHS); } - PointerT operator->() { return &static_cast(this)->operator*(); } - PointerT operator->() const { - return &static_cast(this)->operator*(); - } - ReferenceProxy operator[](DifferenceTypeT n) { - static_assert(IsRandomAccess, - "Subscripting is only defined for random access iterators."); - return ReferenceProxy(static_cast(this)->operator+(n)); + PointerProxy operator->() const { + return static_cast(this)->operator*(); } ReferenceProxy operator[](DifferenceTypeT n) const { static_assert(IsRandomAccess, "Subscripting is only defined for random access iterators."); - return ReferenceProxy(static_cast(this)->operator+(n)); + return static_cast(this)->operator+(n); } }; @@ -330,8 +354,7 @@ public: explicit pointer_iterator(WrappedIteratorT u) : pointer_iterator::iterator_adaptor_base(std::move(u)) {} - T &operator*() { return Ptr = &*this->I; } - const T &operator*() const { return Ptr = &*this->I; } + T &operator*() const { return Ptr = &*this->I; } }; template (ModRefInfo::ModRef)); } +/// Virtual base class for providers of capture information. +struct CaptureInfo { + virtual ~CaptureInfo() = 0; + virtual bool isNotCapturedBeforeOrAt(const Value *Object, + const Instruction *I) = 0; +}; + +/// Context-free CaptureInfo provider, which computes and caches whether an +/// object is captured in the function at all, but does not distinguish whether +/// it was captured before or after the context instruction. +class SimpleCaptureInfo final : public CaptureInfo { + SmallDenseMap IsCapturedCache; + +public: + bool isNotCapturedBeforeOrAt(const Value *Object, + const Instruction *I) override; +}; + +/// Context-sensitive CaptureInfo provider, which computes and caches the +/// earliest common dominator closure of all captures. It provides a good +/// approximation to a precise "captures before" analysis. +class EarliestEscapeInfo final : public CaptureInfo { + DominatorTree &DT; + const LoopInfo &LI; + + /// Map from identified local object to an instruction before which it does + /// not escape, or nullptr if it never escapes. The "earliest" instruction + /// may be a conservative approximation, e.g. the first instruction in the + /// function is always a legal choice. + DenseMap EarliestEscapes; + + /// Reverse map from instruction to the objects it is the earliest escape for. + /// This is used for cache invalidation purposes. + DenseMap> Inst2Obj; + +public: + EarliestEscapeInfo(DominatorTree &DT, const LoopInfo &LI) : DT(DT), LI(LI) {} + + bool isNotCapturedBeforeOrAt(const Value *Object, + const Instruction *I) override; + + void removeInstruction(Instruction *I); +}; + /// Reduced version of MemoryLocation that only stores a pointer and size. /// Used for caching AATags independent BasicAA results. struct AACacheLoc { @@ -425,8 +470,7 @@ public: using AliasCacheT = SmallDenseMap; AliasCacheT AliasCache; - using IsCapturedCacheT = SmallDenseMap; - IsCapturedCacheT IsCapturedCache; + CaptureInfo *CI; /// Query depth used to distinguish recursive queries. unsigned Depth = 0; @@ -439,18 +483,26 @@ public: /// assumption is disproven. SmallVector AssumptionBasedResults; - AAQueryInfo() : AliasCache(), IsCapturedCache() {} + AAQueryInfo(CaptureInfo *CI) : CI(CI) {} /// Create a new AAQueryInfo based on this one, but with the cache cleared. /// This is used for recursive queries across phis, where cache results may /// not be valid. AAQueryInfo withEmptyCache() { - AAQueryInfo NewAAQI; + AAQueryInfo NewAAQI(CI); NewAAQI.Depth = Depth; return NewAAQI; } }; +/// AAQueryInfo that uses SimpleCaptureInfo. +class SimpleAAQueryInfo : public AAQueryInfo { + SimpleCaptureInfo CI; + +public: + SimpleAAQueryInfo() : AAQueryInfo(&CI) {} +}; + class BatchAAResults; class AAResults { @@ -770,7 +822,7 @@ public: /// helpers above. ModRefInfo getModRefInfo(const Instruction *I, const Optional &OptLoc) { - AAQueryInfo AAQIP; + SimpleAAQueryInfo AAQIP; return getModRefInfo(I, OptLoc, AAQIP); } @@ -797,7 +849,7 @@ public: ModRefInfo callCapturesBefore(const Instruction *I, const MemoryLocation &MemLoc, DominatorTree *DT) { - AAQueryInfo AAQIP; + SimpleAAQueryInfo AAQIP; return callCapturesBefore(I, MemLoc, DT, AAQIP); } @@ -896,9 +948,12 @@ private: class BatchAAResults { AAResults &AA; AAQueryInfo AAQI; + SimpleCaptureInfo SimpleCI; public: - BatchAAResults(AAResults &AAR) : AA(AAR), AAQI() {} + BatchAAResults(AAResults &AAR) : AA(AAR), AAQI(&SimpleCI) {} + BatchAAResults(AAResults &AAR, CaptureInfo *CI) : AA(AAR), AAQI(CI) {} + AliasResult alias(const MemoryLocation &LocA, const MemoryLocation &LocB) { return AA.alias(LocA, LocB, AAQI); } diff --git a/llvm/include/llvm/Analysis/AssumeBundleQueries.h b/llvm/include/llvm/Analysis/AssumeBundleQueries.h index 49c0cd89a4db..77da19110246 100644 --- a/llvm/include/llvm/Analysis/AssumeBundleQueries.h +++ b/llvm/include/llvm/Analysis/AssumeBundleQueries.h @@ -20,7 +20,6 @@ #include "llvm/ADT/DenseMap.h" namespace llvm { -class IntrinsicInst; class AssumptionCache; class DominatorTree; @@ -70,15 +69,15 @@ template<> struct DenseMapInfo { using RetainedKnowledgeKey = std::pair; struct MinMax { - unsigned Min; - unsigned Max; + uint64_t Min; + uint64_t Max; }; /// A mapping from intrinsics (=`llvm.assume` calls) to a value range /// (=knowledge) that is encoded in them. How the value range is interpreted /// depends on the RetainedKnowledgeKey that was used to get this out of the /// RetainedKnowledgeMap. -using Assume2KnowledgeMap = DenseMap; +using Assume2KnowledgeMap = DenseMap; using RetainedKnowledgeMap = DenseMap; @@ -100,7 +99,7 @@ void fillMapFromAssume(AssumeInst &Assume, RetainedKnowledgeMap &Result); /// - ArgValue will be 4. struct RetainedKnowledge { Attribute::AttrKind AttrKind = Attribute::None; - unsigned ArgValue = 0; + uint64_t ArgValue = 0; Value *WasOn = nullptr; bool operator==(RetainedKnowledge Other) const { return AttrKind == Other.AttrKind && WasOn == Other.WasOn && diff --git a/llvm/include/llvm/Analysis/AssumptionCache.h b/llvm/include/llvm/Analysis/AssumptionCache.h index 51d04bd8cf02..12dd9b04c932 100644 --- a/llvm/include/llvm/Analysis/AssumptionCache.h +++ b/llvm/include/llvm/Analysis/AssumptionCache.h @@ -29,6 +29,7 @@ namespace llvm { class AssumeInst; class Function; class raw_ostream; +class TargetTransformInfo; class Value; /// A cache of \@llvm.assume calls within a function. @@ -59,6 +60,8 @@ private: /// We track this to lazily populate our assumptions. Function &F; + TargetTransformInfo *TTI; + /// Vector of weak value handles to calls of the \@llvm.assume /// intrinsic. SmallVector AssumeHandles; @@ -103,7 +106,8 @@ private: public: /// Construct an AssumptionCache from a function by scanning all of /// its instructions. - AssumptionCache(Function &F) : F(F) {} + AssumptionCache(Function &F, TargetTransformInfo *TTI = nullptr) + : F(F), TTI(TTI) {} /// This cache is designed to be self-updating and so it should never be /// invalidated. @@ -174,9 +178,7 @@ class AssumptionAnalysis : public AnalysisInfoMixin { public: using Result = AssumptionCache; - AssumptionCache run(Function &F, FunctionAnalysisManager &) { - return AssumptionCache(F); - } + AssumptionCache run(Function &F, FunctionAnalysisManager &); }; /// Printer pass for the \c AssumptionAnalysis results. diff --git a/llvm/include/llvm/Analysis/BasicAliasAnalysis.h b/llvm/include/llvm/Analysis/BasicAliasAnalysis.h index 991c0cbb642a..ed9d1ba4c5a7 100644 --- a/llvm/include/llvm/Analysis/BasicAliasAnalysis.h +++ b/llvm/include/llvm/Analysis/BasicAliasAnalysis.h @@ -13,10 +13,8 @@ #ifndef LLVM_ANALYSIS_BASICALIASANALYSIS_H #define LLVM_ANALYSIS_BASICALIASANALYSIS_H -#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallPtrSet.h" -#include "llvm/ADT/SmallVector.h" #include "llvm/Analysis/AliasAnalysis.h" #include "llvm/IR/PassManager.h" #include "llvm/Pass.h" @@ -28,7 +26,6 @@ namespace llvm { struct AAMDNodes; -class APInt; class AssumptionCache; class BasicBlock; class DataLayout; @@ -98,71 +95,7 @@ public: FunctionModRefBehavior getModRefBehavior(const Function *Fn); private: - // A linear transformation of a Value; this class represents ZExt(SExt(V, - // SExtBits), ZExtBits) * Scale + Offset. - struct VariableGEPIndex { - // An opaque Value - we can't decompose this further. - const Value *V; - - // We need to track what extensions we've done as we consider the same Value - // with different extensions as different variables in a GEP's linear - // expression; - // e.g.: if V == -1, then sext(x) != zext(x). - unsigned ZExtBits; - unsigned SExtBits; - - APInt Scale; - - // Context instruction to use when querying information about this index. - const Instruction *CxtI; - - /// True if all operations in this expression are NSW. - bool IsNSW; - - void dump() const { - print(dbgs()); - dbgs() << "\n"; - } - void print(raw_ostream &OS) const { - OS << "(V=" << V->getName() - << ", zextbits=" << ZExtBits - << ", sextbits=" << SExtBits - << ", scale=" << Scale << ")"; - } - }; - - // Represents the internal structure of a GEP, decomposed into a base pointer, - // constant offsets, and variable scaled indices. - struct DecomposedGEP { - // Base pointer of the GEP - const Value *Base; - // Total constant offset from base. - APInt Offset; - // Scaled variable (non-constant) indices. - SmallVector VarIndices; - // Is GEP index scale compile-time constant. - bool HasCompileTimeConstantScale; - // Are all operations inbounds GEPs or non-indexing operations? - // (None iff expression doesn't involve any geps) - Optional InBounds; - - void dump() const { - print(dbgs()); - dbgs() << "\n"; - } - void print(raw_ostream &OS) const { - OS << "(DecomposedGEP Base=" << Base->getName() - << ", Offset=" << Offset - << ", VarIndices=["; - for (size_t i = 0; i < VarIndices.size(); i++) { - if (i != 0) - OS << ", "; - VarIndices[i].print(OS); - } - OS << "], HasCompileTimeConstantScale=" << HasCompileTimeConstantScale - << ")"; - } - }; + struct DecomposedGEP; /// Tracks phi nodes we have visited. /// @@ -187,10 +120,6 @@ private: DecomposeGEPExpression(const Value *V, const DataLayout &DL, AssumptionCache *AC, DominatorTree *DT); - static bool isGEPBaseAtNegativeOffset(const GEPOperator *GEPOp, - const DecomposedGEP &DecompGEP, const DecomposedGEP &DecompObject, - LocationSize ObjectAccessSize); - /// A Heuristic for aliasGEP that searches for a constant offset /// between the variables. /// @@ -200,15 +129,14 @@ private: /// However, we know that, for all %x, zext(%x) != zext(%x + 1), even if /// the addition overflows. bool - constantOffsetHeuristic(const SmallVectorImpl &VarIndices, - LocationSize V1Size, LocationSize V2Size, - const APInt &BaseOffset, AssumptionCache *AC, + constantOffsetHeuristic(const DecomposedGEP &GEP, LocationSize V1Size, + LocationSize V2Size, AssumptionCache *AC, DominatorTree *DT); bool isValueEqualInPotentialCycles(const Value *V1, const Value *V2); - void GetIndexDifference(SmallVectorImpl &Dest, - const SmallVectorImpl &Src); + void subtractDecomposedGEPs(DecomposedGEP &DestGEP, + const DecomposedGEP &SrcGEP); AliasResult aliasGEP(const GEPOperator *V1, LocationSize V1Size, const Value *V2, LocationSize V2Size, diff --git a/llvm/include/llvm/Analysis/CGSCCPassManager.h b/llvm/include/llvm/Analysis/CGSCCPassManager.h index e361cccef960..7cf172dc1dd1 100644 --- a/llvm/include/llvm/Analysis/CGSCCPassManager.h +++ b/llvm/include/llvm/Analysis/CGSCCPassManager.h @@ -20,7 +20,7 @@ /// A secondary more general goal is to be able to isolate optimization on /// unrelated parts of the IR module. This is useful to ensure our /// optimizations are principled and don't miss oportunities where refinement -/// of one part of the module influence transformations in another part of the +/// of one part of the module influences transformations in another part of the /// module. But this is also useful if we want to parallelize the optimizations /// across common large module graph shapes which tend to be very wide and have /// large regions of unrelated cliques. @@ -161,6 +161,12 @@ struct RequireAnalysisPass(C, CG); return PreservedAnalyses::all(); } + void printPipeline(raw_ostream &OS, + function_ref MapClassName2PassName) { + auto ClassName = AnalysisT::name(); + auto PassName = MapClassName2PassName(ClassName); + OS << "require<" << PassName << ">"; + } }; /// A proxy from a \c CGSCCAnalysisManager to a \c Module. @@ -215,7 +221,7 @@ using ModuleAnalysisManagerCGSCCProxy = LazyCallGraph &>; /// Support structure for SCC passes to communicate updates the call graph back -/// to the CGSCC pass manager infrsatructure. +/// to the CGSCC pass manager infrastructure. /// /// The CGSCC pass manager runs SCC passes which are allowed to update the call /// graph and SCC structures. This means the structure the pass manager works @@ -274,22 +280,22 @@ struct CGSCCUpdateResult { /// If non-null, the updated current \c RefSCC being processed. /// - /// This is set when a graph refinement takes place an the "current" point in - /// the graph moves "down" or earlier in the post-order walk. This will often - /// cause the "current" RefSCC to be a newly created RefSCC object and the - /// old one to be added to the above worklist. When that happens, this + /// This is set when a graph refinement takes place and the "current" point + /// in the graph moves "down" or earlier in the post-order walk. This will + /// often cause the "current" RefSCC to be a newly created RefSCC object and + /// the old one to be added to the above worklist. When that happens, this /// pointer is non-null and can be used to continue processing the "top" of /// the post-order walk. LazyCallGraph::RefSCC *UpdatedRC; /// If non-null, the updated current \c SCC being processed. /// - /// This is set when a graph refinement takes place an the "current" point in - /// the graph moves "down" or earlier in the post-order walk. This will often - /// cause the "current" SCC to be a newly created SCC object and the old one - /// to be added to the above worklist. When that happens, this pointer is - /// non-null and can be used to continue processing the "top" of the - /// post-order walk. + /// This is set when a graph refinement takes place and the "current" point + /// in the graph moves "down" or earlier in the post-order walk. This will + /// often cause the "current" SCC to be a newly created SCC object and the + /// old one to be added to the above worklist. When that happens, this + /// pointer is non-null and can be used to continue processing the "top" of + /// the post-order walk. LazyCallGraph::SCC *UpdatedC; /// Preserved analyses across SCCs. @@ -298,7 +304,7 @@ struct CGSCCUpdateResult { /// (changing both the CG structure and the function IR itself). However, /// this means we need to take special care to correctly mark what analyses /// are preserved *across* SCCs. We have to track this out-of-band here - /// because within the main `PassManeger` infrastructure we need to mark + /// because within the main `PassManager` infrastructure we need to mark /// everything within an SCC as preserved in order to avoid repeatedly /// invalidating the same analyses as we unnest pass managers and adaptors. /// So we track the cross-SCC version of the preserved analyses here from any @@ -363,6 +369,13 @@ public: /// Runs the CGSCC pass across every SCC in the module. PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); + void printPipeline(raw_ostream &OS, + function_ref MapClassName2PassName) { + OS << "cgscc("; + Pass->printPipeline(OS, MapClassName2PassName); + OS << ")"; + } + static bool isRequired() { return true; } private: @@ -377,8 +390,11 @@ createModuleToPostOrderCGSCCPassAdaptor(CGSCCPassT &&Pass) { using PassModelT = detail::PassModel; + // Do not use make_unique, it causes too many template instantiations, + // causing terrible compile times. return ModuleToPostOrderCGSCCPassAdaptor( - std::make_unique(std::forward(Pass))); + std::unique_ptr( + new PassModelT(std::forward(Pass)))); } /// A proxy from a \c FunctionAnalysisManager to an \c SCC. @@ -461,11 +477,14 @@ class CGSCCToFunctionPassAdaptor public: using PassConceptT = detail::PassConcept; - explicit CGSCCToFunctionPassAdaptor(std::unique_ptr Pass) - : Pass(std::move(Pass)) {} + explicit CGSCCToFunctionPassAdaptor(std::unique_ptr Pass, + bool EagerlyInvalidate, bool NoRerun) + : Pass(std::move(Pass)), EagerlyInvalidate(EagerlyInvalidate), + NoRerun(NoRerun) {} CGSCCToFunctionPassAdaptor(CGSCCToFunctionPassAdaptor &&Arg) - : Pass(std::move(Arg.Pass)) {} + : Pass(std::move(Arg.Pass)), EagerlyInvalidate(Arg.EagerlyInvalidate), + NoRerun(Arg.NoRerun) {} friend void swap(CGSCCToFunctionPassAdaptor &LHS, CGSCCToFunctionPassAdaptor &RHS) { @@ -481,24 +500,56 @@ public: PreservedAnalyses run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, LazyCallGraph &CG, CGSCCUpdateResult &UR); + void printPipeline(raw_ostream &OS, + function_ref MapClassName2PassName) { + OS << "function"; + if (EagerlyInvalidate) + OS << ""; + OS << "("; + Pass->printPipeline(OS, MapClassName2PassName); + OS << ")"; + } + static bool isRequired() { return true; } private: std::unique_ptr Pass; + bool EagerlyInvalidate; + bool NoRerun; }; /// A function to deduce a function pass type and wrap it in the /// templated adaptor. template CGSCCToFunctionPassAdaptor -createCGSCCToFunctionPassAdaptor(FunctionPassT &&Pass) { +createCGSCCToFunctionPassAdaptor(FunctionPassT &&Pass, + bool EagerlyInvalidate = false, + bool NoRerun = false) { using PassModelT = detail::PassModel; + // Do not use make_unique, it causes too many template instantiations, + // causing terrible compile times. return CGSCCToFunctionPassAdaptor( - std::make_unique(std::forward(Pass))); + std::unique_ptr( + new PassModelT(std::forward(Pass))), + EagerlyInvalidate, NoRerun); } +// A marker to determine if function passes should be run on a function within a +// CGSCCToFunctionPassAdaptor. This is used to prevent running an expensive +// function pass (manager) on a function multiple times if SCC mutations cause a +// function to be visited multiple times and the function is not modified by +// other SCC passes. +class ShouldNotRunFunctionPassesAnalysis + : public AnalysisInfoMixin { +public: + static AnalysisKey Key; + struct Result {}; + + Result run(Function &F, FunctionAnalysisManager &FAM) { return Result(); } +}; + /// A helper that repeats an SCC pass each time an indirect call is refined to /// a direct call by that pass. /// @@ -528,6 +579,13 @@ public: PreservedAnalyses run(LazyCallGraph::SCC &InitialC, CGSCCAnalysisManager &AM, LazyCallGraph &CG, CGSCCUpdateResult &UR); + void printPipeline(raw_ostream &OS, + function_ref MapClassName2PassName) { + OS << "devirt<" << MaxIterations << ">("; + Pass->printPipeline(OS, MapClassName2PassName); + OS << ")"; + } + private: std::unique_ptr Pass; int MaxIterations; @@ -541,8 +599,11 @@ DevirtSCCRepeatedPass createDevirtSCCRepeatedPass(CGSCCPassT &&Pass, using PassModelT = detail::PassModel; + // Do not use make_unique, it causes too many template instantiations, + // causing terrible compile times. return DevirtSCCRepeatedPass( - std::make_unique(std::forward(Pass)), + std::unique_ptr( + new PassModelT(std::forward(Pass))), MaxIterations); } diff --git a/llvm/include/llvm/Analysis/CaptureTracking.h b/llvm/include/llvm/Analysis/CaptureTracking.h index 9da5f18e944b..50d12db7a1c3 100644 --- a/llvm/include/llvm/Analysis/CaptureTracking.h +++ b/llvm/include/llvm/Analysis/CaptureTracking.h @@ -22,6 +22,8 @@ namespace llvm { class DataLayout; class Instruction; class DominatorTree; + class LoopInfo; + class Function; /// getDefaultMaxUsesToExploreForCaptureTracking - Return default value of /// the maximal number of uses to explore before giving up. It is used by @@ -55,10 +57,25 @@ namespace llvm { /// MaxUsesToExplore specifies how many uses the analysis should explore for /// one value before giving up due too "too many uses". If MaxUsesToExplore /// is zero, a default value is assumed. - bool PointerMayBeCapturedBefore( - const Value *V, bool ReturnCaptures, bool StoreCaptures, - const Instruction *I, const DominatorTree *DT, bool IncludeI = false, - unsigned MaxUsesToExplore = 0); + bool PointerMayBeCapturedBefore(const Value *V, bool ReturnCaptures, + bool StoreCaptures, const Instruction *I, + const DominatorTree *DT, + bool IncludeI = false, + unsigned MaxUsesToExplore = 0, + const LoopInfo *LI = nullptr); + + // Returns the 'earliest' instruction that captures \p V in \F. An instruction + // A is considered earlier than instruction B, if A dominates B. If 2 escapes + // do not dominate each other, the terminator of the common dominator is + // chosen. If not all uses can be analyzed, the earliest escape is set to + // the first instruction in the function entry block. If \p V does not escape, + // nullptr is returned. Note that the caller of the function has to ensure + // that the instruction the result value is compared against is not in a + // cycle. + Instruction *FindEarliestCapture(const Value *V, Function &F, + bool ReturnCaptures, bool StoreCaptures, + const DominatorTree &DT, + unsigned MaxUsesToExplore = 0); /// This callback is used in conjunction with PointerMayBeCaptured. In /// addition to the interface here, you'll need to provide your own getters diff --git a/llvm/include/llvm/Analysis/ConstantFolding.h b/llvm/include/llvm/Analysis/ConstantFolding.h index 62742fdf9a91..45fb879f0c1f 100644 --- a/llvm/include/llvm/Analysis/ConstantFolding.h +++ b/llvm/include/llvm/Analysis/ConstantFolding.h @@ -128,10 +128,25 @@ Constant *ConstantFoldExtractElementInstruction(Constant *Val, Constant *Idx); Constant *ConstantFoldShuffleVectorInstruction(Constant *V1, Constant *V2, ArrayRef Mask); -/// ConstantFoldLoadFromConstPtr - Return the value that a load from C would -/// produce if it is constant and determinable. If this is not determinable, -/// return null. -Constant *ConstantFoldLoadFromConstPtr(Constant *C, Type *Ty, const DataLayout &DL); +/// Extract value of C at the given Offset reinterpreted as Ty. If bits past +/// the end of C are accessed, they are assumed to be poison. +Constant *ConstantFoldLoadFromConst(Constant *C, Type *Ty, const APInt &Offset, + const DataLayout &DL); + +/// Extract value of C reinterpreted as Ty. Same as previous API with zero +/// offset. +Constant *ConstantFoldLoadFromConst(Constant *C, Type *Ty, + const DataLayout &DL); + +/// Return the value that a load from C with offset Offset would produce if it +/// is constant and determinable. If this is not determinable, return null. +Constant *ConstantFoldLoadFromConstPtr(Constant *C, Type *Ty, APInt Offset, + const DataLayout &DL); + +/// Return the value that a load from C would produce if it is constant and +/// determinable. If this is not determinable, return null. +Constant *ConstantFoldLoadFromConstPtr(Constant *C, Type *Ty, + const DataLayout &DL); /// ConstantFoldLoadThroughGEPConstantExpr - Given a constant and a /// getelementptr constantexpr, return the constant value being addressed by the @@ -140,13 +155,6 @@ Constant *ConstantFoldLoadThroughGEPConstantExpr(Constant *C, ConstantExpr *CE, Type *Ty, const DataLayout &DL); -/// ConstantFoldLoadThroughGEPIndices - Given a constant and getelementptr -/// indices (with an *implied* zero pointer index that is not in the list), -/// return the constant value being addressed by a virtual load, or null if -/// something is funny and we can't decide. -Constant *ConstantFoldLoadThroughGEPIndices(Constant *C, - ArrayRef Indices); - /// canConstantFoldCallTo - Return true if its even possible to fold a call to /// the specified function. bool canConstantFoldCallTo(const CallBase *Call, const Function *F); diff --git a/llvm/include/llvm/Analysis/CostModel.h b/llvm/include/llvm/Analysis/CostModel.h new file mode 100644 index 000000000000..649168050cec --- /dev/null +++ b/llvm/include/llvm/Analysis/CostModel.h @@ -0,0 +1,26 @@ +//===- CostModel.h - --------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_ANALYSIS_COSTMODEL_H +#define LLVM_ANALYSIS_COSTMODEL_H + +#include "llvm/IR/PassManager.h" + +namespace llvm { +/// Printer pass for cost modeling results. +class CostModelPrinterPass : public PassInfoMixin { + raw_ostream &OS; + +public: + explicit CostModelPrinterPass(raw_ostream &OS) : OS(OS) {} + + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); +}; +} // end namespace llvm + +#endif // LLVM_ANALYSIS_COSTMODEL_H diff --git a/llvm/include/llvm/Analysis/Delinearization.h b/llvm/include/llvm/Analysis/Delinearization.h index 2658b6bbc80c..6e942530f253 100644 --- a/llvm/include/llvm/Analysis/Delinearization.h +++ b/llvm/include/llvm/Analysis/Delinearization.h @@ -16,10 +16,115 @@ #ifndef LLVM_ANALYSIS_DELINEARIZATION_H #define LLVM_ANALYSIS_DELINEARIZATION_H +#include "llvm/ADT/SmallVector.h" #include "llvm/IR/PassManager.h" #include "llvm/Support/raw_ostream.h" namespace llvm { +class GetElementPtrInst; +class ScalarEvolution; +class SCEV; + +/// Compute the array dimensions Sizes from the set of Terms extracted from +/// the memory access function of this SCEVAddRecExpr (second step of +/// delinearization). +void findArrayDimensions(ScalarEvolution &SE, + SmallVectorImpl &Terms, + SmallVectorImpl &Sizes, + const SCEV *ElementSize); + +/// Collect parametric terms occurring in step expressions (first step of +/// delinearization). +void collectParametricTerms(ScalarEvolution &SE, const SCEV *Expr, + SmallVectorImpl &Terms); + +/// Return in Subscripts the access functions for each dimension in Sizes +/// (third step of delinearization). +void computeAccessFunctions(ScalarEvolution &SE, const SCEV *Expr, + SmallVectorImpl &Subscripts, + SmallVectorImpl &Sizes); +/// Split this SCEVAddRecExpr into two vectors of SCEVs representing the +/// subscripts and sizes of an array access. +/// +/// The delinearization is a 3 step process: the first two steps compute the +/// sizes of each subscript and the third step computes the access functions +/// for the delinearized array: +/// +/// 1. Find the terms in the step functions +/// 2. Compute the array size +/// 3. Compute the access function: divide the SCEV by the array size +/// starting with the innermost dimensions found in step 2. The Quotient +/// is the SCEV to be divided in the next step of the recursion. The +/// Remainder is the subscript of the innermost dimension. Loop over all +/// array dimensions computed in step 2. +/// +/// To compute a uniform array size for several memory accesses to the same +/// object, one can collect in step 1 all the step terms for all the memory +/// accesses, and compute in step 2 a unique array shape. This guarantees +/// that the array shape will be the same across all memory accesses. +/// +/// FIXME: We could derive the result of steps 1 and 2 from a description of +/// the array shape given in metadata. +/// +/// Example: +/// +/// A[][n][m] +/// +/// for i +/// for j +/// for k +/// A[j+k][2i][5i] = +/// +/// The initial SCEV: +/// +/// A[{{{0,+,2*m+5}_i, +, n*m}_j, +, n*m}_k] +/// +/// 1. Find the different terms in the step functions: +/// -> [2*m, 5, n*m, n*m] +/// +/// 2. Compute the array size: sort and unique them +/// -> [n*m, 2*m, 5] +/// find the GCD of all the terms = 1 +/// divide by the GCD and erase constant terms +/// -> [n*m, 2*m] +/// GCD = m +/// divide by GCD -> [n, 2] +/// remove constant terms +/// -> [n] +/// size of the array is A[unknown][n][m] +/// +/// 3. Compute the access function +/// a. Divide {{{0,+,2*m+5}_i, +, n*m}_j, +, n*m}_k by the innermost size m +/// Quotient: {{{0,+,2}_i, +, n}_j, +, n}_k +/// Remainder: {{{0,+,5}_i, +, 0}_j, +, 0}_k +/// The remainder is the subscript of the innermost array dimension: [5i]. +/// +/// b. Divide Quotient: {{{0,+,2}_i, +, n}_j, +, n}_k by next outer size n +/// Quotient: {{{0,+,0}_i, +, 1}_j, +, 1}_k +/// Remainder: {{{0,+,2}_i, +, 0}_j, +, 0}_k +/// The Remainder is the subscript of the next array dimension: [2i]. +/// +/// The subscript of the outermost dimension is the Quotient: [j+k]. +/// +/// Overall, we have: A[][n][m], and the access function: A[j+k][2i][5i]. +void delinearize(ScalarEvolution &SE, const SCEV *Expr, + SmallVectorImpl &Subscripts, + SmallVectorImpl &Sizes, const SCEV *ElementSize); + +/// Gathers the individual index expressions from a GEP instruction. +/// +/// This function optimistically assumes the GEP references into a fixed size +/// array. If this is actually true, this function returns a list of array +/// subscript expressions in \p Subscripts and a list of integers describing +/// the size of the individual array dimensions in \p Sizes. Both lists have +/// either equal length or the size list is one element shorter in case there +/// is no known size available for the outermost array dimension. Returns true +/// if successful and false otherwise. +bool getIndexExpressionsFromGEP(ScalarEvolution &SE, + const GetElementPtrInst *GEP, + SmallVectorImpl &Subscripts, + SmallVectorImpl &Sizes); + struct DelinearizationPrinterPass : public PassInfoMixin { explicit DelinearizationPrinterPass(raw_ostream &OS); diff --git a/llvm/include/llvm/Analysis/HeatUtils.h b/llvm/include/llvm/Analysis/HeatUtils.h index b665e211c6ac..9ecbbaf318da 100644 --- a/llvm/include/llvm/Analysis/HeatUtils.h +++ b/llvm/include/llvm/Analysis/HeatUtils.h @@ -1,9 +1,8 @@ //===-- HeatUtils.h - Utility for printing heat colors ----------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/llvm/include/llvm/Analysis/IRSimilarityIdentifier.h b/llvm/include/llvm/Analysis/IRSimilarityIdentifier.h index b623b9ca58d8..51c5c620230b 100644 --- a/llvm/include/llvm/Analysis/IRSimilarityIdentifier.h +++ b/llvm/include/llvm/Analysis/IRSimilarityIdentifier.h @@ -110,7 +110,8 @@ enum InstrType { Legal, Illegal, Invisible }; /// by \ref isSameOperationAs. /// TODO: Handle GetElementPtrInsts, as some of the operands have to be the /// exact same, and some do not. -struct IRInstructionData : ilist_node { +struct IRInstructionData + : ilist_node> { /// The source Instruction that is being wrapped. Instruction *Inst = nullptr; @@ -127,12 +128,41 @@ struct IRInstructionData : ilist_node { /// to a less than form. It is None otherwise. Optional RevisedPredicate; + /// This structure holds the distances of how far "ahead of" or "behind" the + /// target blocks of a branch, or the incoming blocks of a phi nodes are. + /// If the value is negative, it means that the block was registered before + /// the block of this instruction in terms of blocks in the function. + /// Code Example: + /// \code + /// block_1: + /// br i1 %0, label %block_2, label %block_3 + /// block_2: + /// br i1 %1, label %block_1, label %block_2 + /// block_3: + /// br i1 %2, label %block_2, label %block_1 + /// ; Replacing the labels with relative values, this becomes: + /// block_1: + /// br i1 %0, distance 1, distance 2 + /// block_2: + /// br i1 %1, distance -1, distance 0 + /// block_3: + /// br i1 %2, distance -1, distance -2 + /// \endcode + /// Taking block_2 as our example, block_1 is "behind" block_2, and block_2 is + /// "ahead" of block_2. + SmallVector RelativeBlockLocations; + /// Gather the information that is difficult to gather for an Instruction, or /// is changed. i.e. the operands of an Instruction and the Types of those /// operands. This extra information allows for similarity matching to make /// assertions that allow for more flexibility when checking for whether an /// Instruction performs the same operation. IRInstructionData(Instruction &I, bool Legality, IRInstructionDataList &IDL); + IRInstructionData(IRInstructionDataList &IDL); + + /// Fills data stuctures for IRInstructionData when it is constructed from a + // reference or a pointer. + void initializeInstruction(); /// Get the predicate that the compare instruction is using for hashing the /// instruction. the IRInstructionData must be wrapping a CmpInst. @@ -145,6 +175,16 @@ struct IRInstructionData : ilist_node { /// \return the consistent comparison predicate. static CmpInst::Predicate predicateForConsistency(CmpInst *CI); + /// For an IRInstructionData containing a branch, finds the + /// relative distances from the source basic block to the target by taking + /// the difference of the number assigned to the current basic block and the + /// target basic block of the branch. + /// + /// \param BasicBlockToInteger - The mapping of basic blocks to their location + /// in the module. + void + setBranchSuccessors(DenseMap &BasicBlockToInteger); + /// Hashes \p Value based on its opcode, types, and operand types. /// Two IRInstructionData instances produce the same hash when they perform /// the same operation. @@ -198,7 +238,8 @@ struct IRInstructionData : ilist_node { IRInstructionDataList *IDL = nullptr; }; -struct IRInstructionDataList : simple_ilist {}; +struct IRInstructionDataList + : simple_ilist> {}; /// Compare one IRInstructionData class to another IRInstructionData class for /// whether they are performing a the same operation, and can mapped to the @@ -288,6 +329,10 @@ struct IRInstructionMapper { DenseMap InstructionIntegerMap; + /// A mapping for a basic block in a module to its assigned number/location + /// in the module. + DenseMap BasicBlockToInteger; + /// Set if we added an illegal number in the previous step. /// Since each illegal number is unique, we only need one of them between /// each range of legal numbers. This lets us make sure we don't add more @@ -322,6 +367,14 @@ struct IRInstructionMapper { IRInstructionData *allocateIRInstructionData(Instruction &I, bool Legality, IRInstructionDataList &IDL); + /// Get an empty allocated IRInstructionData struct using the + /// InstDataAllocator. + /// + /// \param IDL - The InstructionDataList that the IRInstructionData is + /// inserted into. + /// \returns An allocated IRInstructionData struct. + IRInstructionData *allocateIRInstructionData(IRInstructionDataList &IDL); + /// Get an allocated IRInstructionDataList object using the IDLAllocator. /// /// \returns An allocated IRInstructionDataList object. @@ -329,6 +382,24 @@ struct IRInstructionMapper { IRInstructionDataList *IDL = nullptr; + /// Assigns values to all the basic blocks in function \p F starting from + /// integer \p BBNumber. + /// + /// \param F - The function containing the basic blocks to assign numbers to. + /// \param BBNumber - The number to start from. + void initializeForBBs(Function &F, unsigned &BBNumber) { + for (BasicBlock &BB : F) + BasicBlockToInteger.insert(std::make_pair(&BB, BBNumber++)); + } + + /// Assigns values to all the basic blocks in Module \p M. + /// \param M - The module containing the basic blocks to assign numbers to. + void initializeForBBs(Module &M) { + unsigned BBNumber = 0; + for (Function &F : M) + initializeForBBs(F, BBNumber); + } + /// Maps the Instructions in a BasicBlock \p BB to legal or illegal integers /// determined by \p InstrType. Two Instructions are mapped to the same value /// if they are close as defined by the InstructionData class above. @@ -386,7 +457,11 @@ struct IRInstructionMapper { InstructionClassification() {} // TODO: Determine a scheme to resolve when the label is similar enough. - InstrType visitBranchInst(BranchInst &BI) { return Illegal; } + InstrType visitBranchInst(BranchInst &BI) { + if (EnableBranches) + return Legal; + return Illegal; + } // TODO: Determine a scheme to resolve when the labels are similar enough. InstrType visitPHINode(PHINode &PN) { return Illegal; } // TODO: Handle allocas. @@ -419,6 +494,10 @@ struct IRInstructionMapper { // TODO: Handle interblock similarity. InstrType visitTerminator(Instruction &I) { return Illegal; } InstrType visitInstruction(Instruction &I) { return Legal; } + + // The flag variable that lets the classifier know whether we should + // allow branches to be checked for similarity. + bool EnableBranches = false; }; /// Maps an Instruction to a member of InstrType. @@ -488,6 +567,12 @@ private: DenseMap ValueToNumber; /// Stores the mapping of the number to the value assigned this number. DenseMap NumberToValue; + /// Stores the mapping of a value's number to canonical numbering in the + /// candidate's respective similarity group. + DenseMap NumberToCanonNum; + /// Stores the mapping of canonical number in the candidate's respective + /// similarity group to a value number. + DenseMap CanonNumToNumber; /// @} public: @@ -506,13 +591,27 @@ public: static bool isSimilar(const IRSimilarityCandidate &A, const IRSimilarityCandidate &B); - /// \param A - The first IRInstructionCandidate to compare. - /// \param B - The second IRInstructionCandidate to compare. + /// \param [in] A - The first IRInstructionCandidate to compare. + /// \param [in] B - The second IRInstructionCandidate to compare. /// \returns True when every IRInstructionData in \p A is structurally similar /// to \p B. static bool compareStructure(const IRSimilarityCandidate &A, const IRSimilarityCandidate &B); + /// \param [in] A - The first IRInstructionCandidate to compare. + /// \param [in] B - The second IRInstructionCandidate to compare. + /// \param [in,out] ValueNumberMappingA - A mapping of value numbers from + /// candidate \p A to candidate \B. + /// \param [in,out] ValueNumberMappingB - A mapping of value numbers from + /// candidate \p B to candidate \A. + /// \returns True when every IRInstructionData in \p A is structurally similar + /// to \p B. + static bool + compareStructure(const IRSimilarityCandidate &A, + const IRSimilarityCandidate &B, + DenseMap> &ValueNumberMappingA, + DenseMap> &ValueNumberMappingB); + struct OperandMapping { /// The IRSimilarityCandidate that holds the instruction the OperVals were /// pulled from. @@ -526,6 +625,21 @@ public: DenseMap> &ValueNumberMapping; }; + /// A helper struct to hold the candidate, for a branch instruction, the + /// relative location of a label, and the label itself. This is mostly to + /// group the values together before passing them as a bundle to a function. + struct RelativeLocMapping { + /// The IRSimilarityCandidate that holds the instruction the relative + /// location was pulled from. + const IRSimilarityCandidate &IRSC; + + /// The relative location to be analyzed. + int RelativeLocation; + + /// The corresponding value. + Value *OperVal; + }; + /// Compare the operands in \p A and \p B and check that the current mapping /// of global value numbers from \p A to \p B and \p B to \A is consistent. /// @@ -549,6 +663,94 @@ public: static bool compareCommutativeOperandMapping(OperandMapping A, OperandMapping B); + /// Compare the relative locations in \p A and \p B and check that the + /// distances match if both locations are contained in the region, and that + /// the branches both point outside the region if they do not. + /// Example Region: + /// \code + /// entry: + /// br i1 %0, label %block_1, label %block_3 + /// block_0: + /// br i1 %0, label %block_1, label %block_2 + /// block_1: + /// br i1 %0, label %block_2, label %block_3 + /// block_2: + /// br i1 %1, label %block_1, label %block_4 + /// block_3: + /// br i1 %2, label %block_2, label %block_5 + /// \endcode + /// If we compare the branches in block_0 and block_1 the relative values are + /// 1 and 2 for both, so we consider this a match. + /// + /// If we compare the branches in entry and block_0 the relative values are + /// 2 and 3, and 1 and 2 respectively. Since these are not the same we do not + /// consider them a match. + /// + /// If we compare the branches in block_1 and block_2 the relative values are + /// 1 and 2, and -1 and None respectively. As a result we do not consider + /// these to be the same + /// + /// If we compare the branches in block_2 and block_3 the relative values are + /// -1 and None for both. We do consider these to be a match. + /// + /// \param A - The first IRInstructionCandidate, relative location value, + /// and incoming block. + /// \param B - The second IRInstructionCandidate, relative location value, + /// and incoming block. + /// \returns true if the relative locations match. + static bool checkRelativeLocations(RelativeLocMapping A, + RelativeLocMapping B); + + /// Create a mapping from the value numbering to a different separate set of + /// numbers. This will serve as a guide for relating one candidate to another. + /// The canonical number gives use the ability identify which global value + /// number in one candidate relates to the global value number in the other. + /// + /// \param [in, out] CurrCand - The IRSimilarityCandidate to create a + /// canonical numbering for. + static void createCanonicalMappingFor(IRSimilarityCandidate &CurrCand); + + /// Create a mapping for the value numbering of the calling + /// IRSimilarityCandidate, to a different separate set of numbers, based on + /// the canonical ordering in \p SourceCand. These are defined based on the + /// found mappings in \p ToSourceMapping and \p FromSourceMapping. Both of + /// these relationships should have the same information, just in opposite + /// directions. + /// + /// \param [in, out] SourceCand - The IRSimilarityCandidate to create a + /// canonical numbering from. + /// \param ToSourceMapping - The mapping of value numbers from this candidate + /// to \p SourceCand. + /// \param FromSourceMapping - The mapping of value numbers from \p SoureCand + /// to this candidate. + void createCanonicalRelationFrom( + IRSimilarityCandidate &SourceCand, + DenseMap> &ToSourceMapping, + DenseMap> &FromSourceMapping); + + /// \param [in,out] BBSet - The set to track the basic blocks. + void getBasicBlocks(DenseSet &BBSet) const { + for (IRInstructionData &ID : *this) { + BasicBlock *BB = ID.Inst->getParent(); + if (BBSet.contains(BB)) + continue; + BBSet.insert(BB); + } + } + + /// \param [in,out] BBSet - The set to track the basic blocks. + /// \param [in,out] BBList - A list in order of use to track the basic blocks. + void getBasicBlocks(DenseSet &BBSet, + SmallVector &BBList) const { + for (IRInstructionData &ID : *this) { + BasicBlock *BB = ID.Inst->getParent(); + if (BBSet.contains(BB)) + continue; + BBSet.insert(BB); + BBList.push_back(BB); + } + } + /// Compare the start and end indices of the two IRSimilarityCandidates for /// whether they overlap. If the start instruction of one /// IRSimilarityCandidate is less than the end instruction of the other, and @@ -611,6 +813,32 @@ public: return VNIt->second; } + /// Find the canonical number from the global value number \p N stored in the + /// candidate. + /// + /// \param N - The global value number to find the canonical number for. + /// \returns An optional containing the value, and None if it could not be + /// found. + Optional getCanonicalNum(unsigned N) { + DenseMap::iterator NCIt = NumberToCanonNum.find(N); + if (NCIt == NumberToCanonNum.end()) + return None; + return NCIt->second; + } + + /// Find the global value number from the canonical number \p N stored in the + /// candidate. + /// + /// \param N - The canonical number to find the global vlaue number for. + /// \returns An optional containing the value, and None if it could not be + /// found. + Optional fromCanonicalNum(unsigned N) { + DenseMap::iterator CNIt = CanonNumToNumber.find(N); + if (CNIt == CanonNumToNumber.end()) + return None; + return CNIt->second; + } + /// \param RHS -The IRSimilarityCandidate to compare against /// \returns true if the IRSimilarityCandidate is occurs after the /// IRSimilarityCandidate in the program. @@ -623,6 +851,9 @@ public: iterator end() const { return std::next(iterator(back())); } }; +typedef DenseMap>> + CandidateGVNMapping; typedef std::vector SimilarityGroup; typedef std::vector SimilarityGroupList; @@ -651,8 +882,9 @@ typedef std::vector SimilarityGroupList; /// analyzing the module. class IRSimilarityIdentifier { public: - IRSimilarityIdentifier() - : Mapper(&InstDataAllocator, &InstDataListAllocator) {} + IRSimilarityIdentifier(bool MatchBranches = true) + : Mapper(&InstDataAllocator, &InstDataListAllocator), + EnableBranches(MatchBranches) {} private: /// Map the instructions in the module to unsigned integers, using mapping @@ -728,6 +960,10 @@ private: /// instance of IRInstructionData. IRInstructionMapper Mapper; + /// The flag variable that marks whether we should check branches for + /// similarity, or only look within basic blocks. + bool EnableBranches = true; + /// The SimilarityGroups found with the most recent run of \ref /// findSimilarity. None if there is no recent run. Optional SimilarityCandidates; diff --git a/llvm/include/llvm/Analysis/IVDescriptors.h b/llvm/include/llvm/Analysis/IVDescriptors.h index 82e1b14960bd..c26dbc457949 100644 --- a/llvm/include/llvm/Analysis/IVDescriptors.h +++ b/llvm/include/llvm/Analysis/IVDescriptors.h @@ -36,20 +36,24 @@ class DominatorTree; /// These are the kinds of recurrences that we support. enum class RecurKind { - None, ///< Not a recurrence. - Add, ///< Sum of integers. - Mul, ///< Product of integers. - Or, ///< Bitwise or logical OR of integers. - And, ///< Bitwise or logical AND of integers. - Xor, ///< Bitwise or logical XOR of integers. - SMin, ///< Signed integer min implemented in terms of select(cmp()). - SMax, ///< Signed integer max implemented in terms of select(cmp()). - UMin, ///< Unisgned integer min implemented in terms of select(cmp()). - UMax, ///< Unsigned integer max implemented in terms of select(cmp()). - FAdd, ///< Sum of floats. - FMul, ///< Product of floats. - FMin, ///< FP min implemented in terms of select(cmp()). - FMax ///< FP max implemented in terms of select(cmp()). + None, ///< Not a recurrence. + Add, ///< Sum of integers. + Mul, ///< Product of integers. + Or, ///< Bitwise or logical OR of integers. + And, ///< Bitwise or logical AND of integers. + Xor, ///< Bitwise or logical XOR of integers. + SMin, ///< Signed integer min implemented in terms of select(cmp()). + SMax, ///< Signed integer max implemented in terms of select(cmp()). + UMin, ///< Unisgned integer min implemented in terms of select(cmp()). + UMax, ///< Unsigned integer max implemented in terms of select(cmp()). + FAdd, ///< Sum of floats. + FMul, ///< Product of floats. + FMin, ///< FP min implemented in terms of select(cmp()). + FMax, ///< FP max implemented in terms of select(cmp()). + SelectICmp, ///< Integer select(icmp(),x,y) where one of (x,y) is loop + ///< invariant + SelectFCmp ///< Integer select(fcmp(),x,y) where one of (x,y) is loop + ///< invariant }; /// The RecurrenceDescriptor is used to identify recurrences variables in a @@ -112,12 +116,14 @@ public: }; /// Returns a struct describing if the instruction 'I' can be a recurrence - /// variable of type 'Kind'. If the recurrence is a min/max pattern of - /// select(icmp()) this function advances the instruction pointer 'I' from the - /// compare instruction to the select instruction and stores this pointer in - /// 'PatternLastInst' member of the returned struct. - static InstDesc isRecurrenceInstr(Instruction *I, RecurKind Kind, - InstDesc &Prev, FastMathFlags FMF); + /// variable of type 'Kind' for a Loop \p L and reduction PHI \p Phi. + /// If the recurrence is a min/max pattern of select(icmp()) this function + /// advances the instruction pointer 'I' from the compare instruction to the + /// select instruction and stores this pointer in 'PatternLastInst' member of + /// the returned struct. + static InstDesc isRecurrenceInstr(Loop *L, PHINode *Phi, Instruction *I, + RecurKind Kind, InstDesc &Prev, + FastMathFlags FuncFMF); /// Returns true if instruction I has multiple uses in Insts static bool hasMultipleUsesOf(Instruction *I, @@ -127,20 +133,29 @@ public: /// Returns true if all uses of the instruction I is within the Set. static bool areAllUsesIn(Instruction *I, SmallPtrSetImpl &Set); - /// Returns a struct describing if the instruction is a - /// Select(ICmp(X, Y), X, Y) instruction pattern corresponding to a min(X, Y) - /// or max(X, Y). \p Prev specifies the description of an already processed - /// select instruction, so its corresponding cmp can be matched to it. - static InstDesc isMinMaxSelectCmpPattern(Instruction *I, - const InstDesc &Prev); + /// Returns a struct describing if the instruction is a llvm.(s/u)(min/max), + /// llvm.minnum/maxnum or a Select(ICmp(X, Y), X, Y) pair of instructions + /// corresponding to a min(X, Y) or max(X, Y), matching the recurrence kind \p + /// Kind. \p Prev specifies the description of an already processed select + /// instruction, so its corresponding cmp can be matched to it. + static InstDesc isMinMaxPattern(Instruction *I, RecurKind Kind, + const InstDesc &Prev); + + /// Returns a struct describing whether the instruction is either a + /// Select(ICmp(A, B), X, Y), or + /// Select(FCmp(A, B), X, Y) + /// where one of (X, Y) is a loop invariant integer and the other is a PHI + /// value. \p Prev specifies the description of an already processed select + /// instruction, so its corresponding cmp can be matched to it. + static InstDesc isSelectCmpPattern(Loop *Loop, PHINode *OrigPhi, + Instruction *I, InstDesc &Prev); /// Returns a struct describing if the instruction is a /// Select(FCmp(X, Y), (Z = X op PHINode), PHINode) instruction pattern. static InstDesc isConditionalRdxPattern(RecurKind Kind, Instruction *I); /// Returns identity corresponding to the RecurrenceKind. - static Constant *getRecurrenceIdentity(RecurKind K, Type *Tp, - FastMathFlags FMF); + Value *getRecurrenceIdentity(RecurKind K, Type *Tp, FastMathFlags FMF); /// Returns the opcode corresponding to the RecurrenceKind. static unsigned getOpcode(RecurKind Kind); @@ -150,7 +165,7 @@ public: /// non-null, the minimal bit width needed to compute the reduction will be /// computed. static bool AddReductionVar(PHINode *Phi, RecurKind Kind, Loop *TheLoop, - FastMathFlags FMF, + FastMathFlags FuncFMF, RecurrenceDescriptor &RedDes, DemandedBits *DB = nullptr, AssumptionCache *AC = nullptr, @@ -220,6 +235,12 @@ public: return isIntMinMaxRecurrenceKind(Kind) || isFPMinMaxRecurrenceKind(Kind); } + /// Returns true if the recurrence kind is of the form + /// select(cmp(),x,y) where one of (x,y) is loop invariant. + static bool isSelectCmpRecurrenceKind(RecurKind Kind) { + return Kind == RecurKind::SelectICmp || Kind == RecurKind::SelectFCmp; + } + /// Returns the type of the recurrence. This type can be narrower than the /// actual type of the Phi if the recurrence has been type-promoted. Type *getRecurrenceType() const { return RecurrenceType; } @@ -329,6 +350,11 @@ public: : Instruction::BinaryOpsEnd; } + Type *getElementType() const { + assert(IK == IK_PtrInduction && "Only pointer induction has element type"); + return ElementType; + } + /// Returns a reference to the type cast instructions in the induction /// update chain, that are redundant when guarded with a runtime /// SCEV overflow check. @@ -340,6 +366,7 @@ private: /// Private constructor - used by \c isInductionPHI. InductionDescriptor(Value *Start, InductionKind K, const SCEV *Step, BinaryOperator *InductionBinOp = nullptr, + Type *ElementType = nullptr, SmallVectorImpl *Casts = nullptr); /// Start value. @@ -350,6 +377,9 @@ private: const SCEV *Step = nullptr; // Instruction that advances induction variable. BinaryOperator *InductionBinOp = nullptr; + // Element type for pointer induction variables. + // TODO: This can be dropped once support for typed pointers is removed. + Type *ElementType = nullptr; // Instructions used for type-casts of the induction variable, // that are redundant when guarded with a runtime SCEV overflow check. SmallVector RedundantCasts; diff --git a/llvm/include/llvm/Analysis/IVUsers.h b/llvm/include/llvm/Analysis/IVUsers.h index f8ea3bcca229..e2026a4d5875 100644 --- a/llvm/include/llvm/Analysis/IVUsers.h +++ b/llvm/include/llvm/Analysis/IVUsers.h @@ -157,9 +157,6 @@ public: /// dump - This method is used for debugging. void dump() const; - -protected: - bool AddUsersImpl(Instruction *I, SmallPtrSetImpl &SimpleLoopNests); }; Pass *createIVUsersPass(); diff --git a/llvm/include/llvm/Analysis/InlineAdvisor.h b/llvm/include/llvm/Analysis/InlineAdvisor.h index c27aaf0db8f2..9f9bc3a5e71b 100644 --- a/llvm/include/llvm/Analysis/InlineAdvisor.h +++ b/llvm/include/llvm/Analysis/InlineAdvisor.h @@ -22,6 +22,7 @@ class CallBase; class Function; class Module; class OptimizationRemarkEmitter; +struct ReplayInlinerSettings; /// There are 3 scenarios we can use the InlineAdvisor: /// - Default - use manual heuristics. @@ -143,7 +144,11 @@ public: /// be up-to-date wrt previous inlining decisions. \p MandatoryOnly indicates /// only mandatory (always-inline) call sites should be recommended - this /// allows the InlineAdvisor track such inlininings. - /// Returns an InlineAdvice with the inlining recommendation. + /// Returns: + /// - An InlineAdvice with the inlining recommendation. + /// - Null when no recommendation is made (https://reviews.llvm.org/D110658). + /// TODO: Consider removing the Null return scenario by incorporating the + /// SampleProfile inliner into an InlineAdvisor std::unique_ptr getAdvice(CallBase &CB, bool MandatoryOnly = false); @@ -157,6 +162,12 @@ public: /// to prepare for a partial update. virtual void onPassExit() {} + /// Called when the module is invalidated. We let the advisor implementation + /// decide what to refresh - in the case of the development mode + /// implementation, for example, we wouldn't want to delete the whole object + /// and need to re-load the model evaluator. + virtual void onModuleInvalidated() {} + protected: InlineAdvisor(Module &M, FunctionAnalysisManager &FAM); virtual std::unique_ptr getAdviceImpl(CallBase &CB) = 0; @@ -219,15 +230,18 @@ public: InlineAdvisorAnalysis() = default; struct Result { Result(Module &M, ModuleAnalysisManager &MAM) : M(M), MAM(MAM) {} - bool invalidate(Module &, const PreservedAnalyses &, + bool invalidate(Module &, const PreservedAnalyses &PA, ModuleAnalysisManager::Invalidator &) { - // InlineAdvisor must be preserved across analysis invalidations. - return false; + if (Advisor && !PA.areAllPreserved()) + Advisor->onModuleInvalidated(); + // Check whether the analysis has been explicitly invalidated. Otherwise, + // it's stateless and remains preserved. + auto PAC = PA.getChecker(); + return !PAC.preservedWhenStateless(); } bool tryCreate(InlineParams Params, InliningAdvisorMode Mode, - StringRef ReplayFile); + const ReplayInlinerSettings &ReplaySettings); InlineAdvisor *getAdvisor() const { return Advisor.get(); } - void clear() { Advisor.reset(); } private: Module &M; @@ -263,12 +277,16 @@ shouldInline(CallBase &CB, function_ref GetInlineCost, /// Emit ORE message. void emitInlinedInto(OptimizationRemarkEmitter &ORE, DebugLoc DLoc, const BasicBlock *Block, const Function &Callee, - const Function &Caller, const InlineCost &IC, - bool ForProfileContext = false, + const Function &Caller, bool IsMandatory, + function_ref ExtraContext = {}, const char *PassName = nullptr); -/// get call site location as string -std::string getCallSiteLocation(DebugLoc DLoc); +/// Emit ORE message based in cost (default heuristic). +void emitInlinedIntoBasedOnCost(OptimizationRemarkEmitter &ORE, DebugLoc DLoc, + const BasicBlock *Block, const Function &Callee, + const Function &Caller, const InlineCost &IC, + bool ForProfileContext = false, + const char *PassName = nullptr); /// Add location info to ORE message. void addLocationToRemarks(OptimizationRemark &Remark, DebugLoc DLoc); diff --git a/llvm/include/llvm/Analysis/InlineCost.h b/llvm/include/llvm/Analysis/InlineCost.h index 4e1b28d4633f..b22841343b1a 100644 --- a/llvm/include/llvm/Analysis/InlineCost.h +++ b/llvm/include/llvm/Analysis/InlineCost.h @@ -213,6 +213,9 @@ struct InlineParams { /// Indicate whether we should allow inline deferral. Optional EnableDeferral = true; + + /// Indicate whether we allow inlining for recursive call. + Optional AllowRecursiveCall = false; }; /// Generate the parameters to tune the inline cost analysis based only on the diff --git a/llvm/include/llvm/Analysis/InlineOrder.h b/llvm/include/llvm/Analysis/InlineOrder.h new file mode 100644 index 000000000000..def3192356f4 --- /dev/null +++ b/llvm/include/llvm/Analysis/InlineOrder.h @@ -0,0 +1,172 @@ +//===- InlineOrder.h - Inlining order abstraction -*- C++ ---*-------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +#ifndef LLVM_ANALYSIS_INLINEORDER_H +#define LLVM_ANALYSIS_INLINEORDER_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include +#include + +namespace llvm { +class CallBase; +class Function; +class Module; + +template class InlineOrder { +public: + using reference = T &; + using const_reference = const T &; + + virtual ~InlineOrder() {} + + virtual size_t size() = 0; + + virtual void push(const T &Elt) = 0; + + virtual T pop() = 0; + + virtual const_reference front() = 0; + + virtual void erase_if(function_ref Pred) = 0; + + bool empty() { return !size(); } +}; + +template > +class DefaultInlineOrder : public InlineOrder { + using reference = T &; + using const_reference = const T &; + +public: + size_t size() override { return Calls.size() - FirstIndex; } + + void push(const T &Elt) override { Calls.push_back(Elt); } + + T pop() override { + assert(size() > 0); + return Calls[FirstIndex++]; + } + + const_reference front() override { + assert(size() > 0); + return Calls[FirstIndex]; + } + + void erase_if(function_ref Pred) override { + Calls.erase(std::remove_if(Calls.begin() + FirstIndex, Calls.end(), Pred), + Calls.end()); + } + +private: + Container Calls; + size_t FirstIndex = 0; +}; + +class InlineSizePriority { +public: + InlineSizePriority(int Size) : Size(Size) {} + + static bool isMoreDesirable(const InlineSizePriority &S1, + const InlineSizePriority &S2) { + return S1.Size < S2.Size; + } + + static InlineSizePriority evaluate(CallBase *CB) { + Function *Callee = CB->getCalledFunction(); + return InlineSizePriority(Callee->getInstructionCount()); + } + + int Size; +}; + +template +class PriorityInlineOrder : public InlineOrder> { + using T = std::pair; + using HeapT = std::pair; + using reference = T &; + using const_reference = const T &; + + static bool cmp(const HeapT &P1, const HeapT &P2) { + return PriorityT::isMoreDesirable(P2.second, P1.second); + } + + // A call site could become less desirable for inlining because of the size + // growth from prior inlining into the callee. This method is used to lazily + // update the desirability of a call site if it's decreasing. It is only + // called on pop() or front(), not every time the desirability changes. When + // the desirability of the front call site decreases, an updated one would be + // pushed right back into the heap. For simplicity, those cases where + // the desirability of a call site increases are ignored here. + void adjust() { + bool Changed = false; + do { + CallBase *CB = Heap.front().first; + const PriorityT PreviousGoodness = Heap.front().second; + const PriorityT CurrentGoodness = PriorityT::evaluate(CB); + Changed = PriorityT::isMoreDesirable(PreviousGoodness, CurrentGoodness); + if (Changed) { + std::pop_heap(Heap.begin(), Heap.end(), cmp); + Heap.pop_back(); + Heap.push_back({CB, CurrentGoodness}); + std::push_heap(Heap.begin(), Heap.end(), cmp); + } + } while (Changed); + } + +public: + size_t size() override { return Heap.size(); } + + void push(const T &Elt) override { + CallBase *CB = Elt.first; + const int InlineHistoryID = Elt.second; + const PriorityT Goodness = PriorityT::evaluate(CB); + + Heap.push_back({CB, Goodness}); + std::push_heap(Heap.begin(), Heap.end(), cmp); + InlineHistoryMap[CB] = InlineHistoryID; + } + + T pop() override { + assert(size() > 0); + adjust(); + + CallBase *CB = Heap.front().first; + T Result = std::make_pair(CB, InlineHistoryMap[CB]); + InlineHistoryMap.erase(CB); + std::pop_heap(Heap.begin(), Heap.end(), cmp); + Heap.pop_back(); + return Result; + } + + const_reference front() override { + assert(size() > 0); + adjust(); + + CallBase *CB = Heap.front().first; + return *InlineHistoryMap.find(CB); + } + + void erase_if(function_ref Pred) override { + auto PredWrapper = [=](HeapT P) -> bool { + return Pred(std::make_pair(P.first, 0)); + }; + llvm::erase_if(Heap, PredWrapper); + std::make_heap(Heap.begin(), Heap.end(), cmp); + } + +private: + SmallVector Heap; + DenseMap InlineHistoryMap; +}; +} // namespace llvm +#endif // LLVM_ANALYSIS_INLINEORDER_H diff --git a/llvm/include/llvm/Analysis/InstructionSimplify.h b/llvm/include/llvm/Analysis/InstructionSimplify.h index efaf1847276b..f0f8e4bc9175 100644 --- a/llvm/include/llvm/Analysis/InstructionSimplify.h +++ b/llvm/include/llvm/Analysis/InstructionSimplify.h @@ -248,7 +248,7 @@ Value *SimplifySelectInst(Value *Cond, Value *TrueVal, Value *FalseVal, const SimplifyQuery &Q); /// Given operands for a GetElementPtrInst, fold the result or return null. -Value *SimplifyGEPInst(Type *SrcTy, ArrayRef Ops, +Value *SimplifyGEPInst(Type *SrcTy, ArrayRef Ops, bool InBounds, const SimplifyQuery &Q); /// Given operands for an InsertValueInst, fold the result or return null. diff --git a/llvm/include/llvm/Analysis/LazyCallGraph.h b/llvm/include/llvm/Analysis/LazyCallGraph.h index ca276d2f3cf8..0580f4d7b226 100644 --- a/llvm/include/llvm/Analysis/LazyCallGraph.h +++ b/llvm/include/llvm/Analysis/LazyCallGraph.h @@ -145,7 +145,7 @@ public: /// around but clear them. explicit operator bool() const; - /// Returnss the \c Kind of the edge. + /// Returns the \c Kind of the edge. Kind getKind() const; /// Test whether the edge represents a direct call to a function. @@ -307,9 +307,9 @@ public: /// A node in the call graph. /// - /// This represents a single node. It's primary roles are to cache the list of - /// callees, de-duplicate and provide fast testing of whether a function is - /// a callee, and facilitate iteration of child nodes in the graph. + /// This represents a single node. Its primary roles are to cache the list of + /// callees, de-duplicate and provide fast testing of whether a function is a + /// callee, and facilitate iteration of child nodes in the graph. /// /// The node works much like an optional in order to lazily populate the /// edges of each node. Until populated, there are no edges. Once populated, @@ -392,7 +392,7 @@ public: /// Internal helper to directly replace the function with a new one. /// - /// This is used to facilitate tranfsormations which need to replace the + /// This is used to facilitate transformations which need to replace the /// formal Function object but directly move the body and users from one to /// the other. void replaceFunction(Function &NewF); @@ -419,7 +419,7 @@ public: /// outer structure. SCCs do not support mutation of the call graph, that /// must be done through the containing \c RefSCC in order to fully reason /// about the ordering and connections of the graph. - class SCC { + class LLVM_EXTERNAL_VISIBILITY SCC { friend class LazyCallGraph; friend class LazyCallGraph::Node; @@ -435,7 +435,7 @@ public: Nodes.clear(); } - /// Print a short descrtiption useful for debugging or logging. + /// Print a short description useful for debugging or logging. /// /// We print the function names in the SCC wrapped in '()'s and skipping /// the middle functions if there are a large number. @@ -467,9 +467,10 @@ public: /// Verify invariants about the SCC. /// /// This will attempt to validate all of the basic invariants within an - /// SCC, but not that it is a strongly connected componet per-se. Primarily - /// useful while building and updating the graph to check that basic - /// properties are in place rather than having inexplicable crashes later. + /// SCC, but not that it is a strongly connected component per se. + /// Primarily useful while building and updating the graph to check that + /// basic properties are in place rather than having inexplicable crashes + /// later. void verify(); #endif @@ -511,7 +512,7 @@ public: /// Provide a short name by printing this SCC to a std::string. /// - /// This copes with the fact that we don't have a name per-se for an SCC + /// This copes with the fact that we don't have a name per se for an SCC /// while still making the use of this in debugging and logging useful. std::string getName() const { std::string Name; @@ -644,7 +645,7 @@ public: /// Provide a short name by printing this RefSCC to a std::string. /// - /// This copes with the fact that we don't have a name per-se for an RefSCC + /// This copes with the fact that we don't have a name per se for an RefSCC /// while still making the use of this in debugging and logging useful. std::string getName() const { std::string Name; @@ -1085,47 +1086,9 @@ public: /// updates that set with every constant visited. /// /// For each defined function, calls \p Callback with that function. - template static void visitReferences(SmallVectorImpl &Worklist, SmallPtrSetImpl &Visited, - CallbackT Callback) { - while (!Worklist.empty()) { - Constant *C = Worklist.pop_back_val(); - - if (Function *F = dyn_cast(C)) { - if (!F->isDeclaration()) - Callback(*F); - continue; - } - - // The blockaddress constant expression is a weird special case, we can't - // generically walk its operands the way we do for all other constants. - if (BlockAddress *BA = dyn_cast(C)) { - // If we've already visited the function referred to by the block - // address, we don't need to revisit it. - if (Visited.count(BA->getFunction())) - continue; - - // If all of the blockaddress' users are instructions within the - // referred to function, we don't need to insert a cycle. - if (llvm::all_of(BA->users(), [&](User *U) { - if (Instruction *I = dyn_cast(U)) - return I->getFunction() == BA->getFunction(); - return false; - })) - continue; - - // Otherwise we should go visit the referred to function. - Visited.insert(BA->getFunction()); - Worklist.push_back(BA->getFunction()); - continue; - } - - for (Value *Op : C->operand_values()) - if (Visited.insert(cast(Op)).second) - Worklist.push_back(cast(Op)); - } - } + function_ref Callback); ///@} diff --git a/llvm/include/llvm/Analysis/LoopAccessAnalysis.h b/llvm/include/llvm/Analysis/LoopAccessAnalysis.h index 0a0ef1536caf..2b4edfac61fc 100644 --- a/llvm/include/llvm/Analysis/LoopAccessAnalysis.h +++ b/llvm/include/llvm/Analysis/LoopAccessAnalysis.h @@ -177,21 +177,11 @@ public: /// Register the location (instructions are given increasing numbers) /// of a write access. - void addAccess(StoreInst *SI) { - Value *Ptr = SI->getPointerOperand(); - Accesses[MemAccessInfo(Ptr, true)].push_back(AccessIdx); - InstMap.push_back(SI); - ++AccessIdx; - } + void addAccess(StoreInst *SI); /// Register the location (instructions are given increasing numbers) /// of a write access. - void addAccess(LoadInst *LI) { - Value *Ptr = LI->getPointerOperand(); - Accesses[MemAccessInfo(Ptr, false)].push_back(AccessIdx); - InstMap.push_back(LI); - ++AccessIdx; - } + void addAccess(LoadInst *LI); /// Check whether the dependencies between the accesses are safe. /// @@ -664,15 +654,14 @@ Value *stripIntegerCast(Value *V); /// If necessary this method will version the stride of the pointer according /// to \p PtrToStride and therefore add further predicates to \p PSE. /// -/// If \p OrigPtr is not null, use it to look up the stride value instead of \p -/// Ptr. \p PtrToStride provides the mapping between the pointer value and its +/// \p PtrToStride provides the mapping between the pointer value and its /// stride as collected by LoopVectorizationLegality::collectStridedAccess. const SCEV *replaceSymbolicStrideSCEV(PredicatedScalarEvolution &PSE, const ValueToValueMap &PtrToStride, - Value *Ptr, Value *OrigPtr = nullptr); + Value *Ptr); -/// If the pointer has a constant stride return it in units of its -/// element size. Otherwise return zero. +/// If the pointer has a constant stride return it in units of the access type +/// size. Otherwise return zero. /// /// Ensure that it does not wrap in the address space, assuming the predicate /// associated with \p PSE is true. @@ -681,7 +670,8 @@ const SCEV *replaceSymbolicStrideSCEV(PredicatedScalarEvolution &PSE, /// to \p PtrToStride and therefore add further predicates to \p PSE. /// The \p Assume parameter indicates if we are allowed to make additional /// run-time assumptions. -int64_t getPtrStride(PredicatedScalarEvolution &PSE, Value *Ptr, const Loop *Lp, +int64_t getPtrStride(PredicatedScalarEvolution &PSE, Type *AccessTy, Value *Ptr, + const Loop *Lp, const ValueToValueMap &StridesMap = ValueToValueMap(), bool Assume = false, bool ShouldCheckWrap = true); diff --git a/llvm/include/llvm/Analysis/LoopAnalysisManager.h b/llvm/include/llvm/Analysis/LoopAnalysisManager.h index 92db1d67fc4e..bc8a1e74e447 100644 --- a/llvm/include/llvm/Analysis/LoopAnalysisManager.h +++ b/llvm/include/llvm/Analysis/LoopAnalysisManager.h @@ -58,6 +58,7 @@ struct LoopStandardAnalysisResults { TargetLibraryInfo &TLI; TargetTransformInfo &TTI; BlockFrequencyInfo *BFI; + BranchProbabilityInfo *BPI; MemorySSA *MSSA; }; diff --git a/llvm/include/llvm/Analysis/LoopInfo.h b/llvm/include/llvm/Analysis/LoopInfo.h index 164ec50e47bc..15c9d911ab80 100644 --- a/llvm/include/llvm/Analysis/LoopInfo.h +++ b/llvm/include/llvm/Analysis/LoopInfo.h @@ -527,7 +527,7 @@ extern template class LoopBase; /// Represents a single loop in the control flow graph. Note that not all SCCs /// in the CFG are necessarily loops. -class Loop : public LoopBase { +class LLVM_EXTERNAL_VISIBILITY Loop : public LoopBase { public: /// A range representing the start and end location of a loop. class LocRange { @@ -950,7 +950,7 @@ public: /// /// Note that because loops form a forest of trees, preorder is equivalent to /// reverse postorder. - SmallVector getLoopsInPreorder(); + SmallVector getLoopsInPreorder() const; /// Return all of the loops in the function in preorder across the loop /// nests, with siblings in *reverse* program order. @@ -960,7 +960,7 @@ public: /// /// Also note that this is *not* a reverse preorder. Only the siblings are in /// reverse program order. - SmallVector getLoopsInReverseSiblingPreorder(); + SmallVector getLoopsInReverseSiblingPreorder() const; /// Return the inner most loop that BB lives in. If a basic block is in no /// loop (for example the entry node), null is returned. @@ -1213,6 +1213,13 @@ public: }; +/// Enable verification of loop info. +/// +/// The flag enables checks which are expensive and are disabled by default +/// unless the `EXPENSIVE_CHECKS` macro is defined. The `-verify-loop-info` +/// flag allows the checks to be enabled selectively without re-compilation. +extern bool VerifyLoopInfo; + // Allow clients to walk the list of nested loops... template <> struct GraphTraits { typedef const Loop *NodeRef; @@ -1305,6 +1312,10 @@ bool getBooleanLoopAttribute(const Loop *TheLoop, StringRef Name); llvm::Optional getOptionalIntLoopAttribute(const Loop *TheLoop, StringRef Name); +/// Find named metadata for a loop with an integer value. Return \p Default if +/// not set. +int getIntLoopAttribute(const Loop *TheLoop, StringRef Name, int Default = 0); + /// Find string metadata for loop /// /// If it has a value (e.g. {"llvm.distribute", 1} return the value as an diff --git a/llvm/include/llvm/Analysis/LoopInfoImpl.h b/llvm/include/llvm/Analysis/LoopInfoImpl.h index 2cc9afb7c2cd..b8b8330d0fe1 100644 --- a/llvm/include/llvm/Analysis/LoopInfoImpl.h +++ b/llvm/include/llvm/Analysis/LoopInfoImpl.h @@ -574,7 +574,8 @@ void LoopInfoBase::analyze(const DomTreeBase &DomTree) { } template -SmallVector LoopInfoBase::getLoopsInPreorder() { +SmallVector +LoopInfoBase::getLoopsInPreorder() const { SmallVector PreOrderLoops, PreOrderWorklist; // The outer-most loop actually goes into the result in the same relative // order as we walk it. But LoopInfo stores the top level loops in reverse @@ -592,7 +593,7 @@ SmallVector LoopInfoBase::getLoopsInPreorder() { template SmallVector -LoopInfoBase::getLoopsInReverseSiblingPreorder() { +LoopInfoBase::getLoopsInReverseSiblingPreorder() const { SmallVector PreOrderLoops, PreOrderWorklist; // The outer-most loop actually goes into the result in the same relative // order as we walk it. LoopInfo stores the top level loops in reverse diff --git a/llvm/include/llvm/Analysis/LoopNestAnalysis.h b/llvm/include/llvm/Analysis/LoopNestAnalysis.h index 9a749a1c8eae..3d4a064cf7e3 100644 --- a/llvm/include/llvm/Analysis/LoopNestAnalysis.h +++ b/llvm/include/llvm/Analysis/LoopNestAnalysis.h @@ -21,11 +21,14 @@ namespace llvm { using LoopVectorTy = SmallVector; + class LPMUpdater; /// This class represents a loop nest and can be used to query its properties. -class LoopNest { +class LLVM_EXTERNAL_VISIBILITY LoopNest { public: + using InstrVectorTy = SmallVector; + /// Construct a loop nest rooted by loop \p Root. LoopNest(Loop &Root, ScalarEvolution &SE); @@ -48,6 +51,12 @@ public: static bool arePerfectlyNested(const Loop &OuterLoop, const Loop &InnerLoop, ScalarEvolution &SE); + /// Return a vector of instructions that prevent the LoopNest given + /// by loops \p OuterLoop and \p InnerLoop from being perfect. + static InstrVectorTy getInterveningInstructions(const Loop &OuterLoop, + const Loop &InnerLoop, + ScalarEvolution &SE); + /// Return the maximum nesting depth of the loop nest rooted by loop \p Root. /// For example given the loop nest: /// \code @@ -150,6 +159,17 @@ public: protected: const unsigned MaxPerfectDepth; // maximum perfect nesting depth level. LoopVectorTy Loops; // the loops in the nest (in breadth first order). + +private: + enum LoopNestEnum { + PerfectLoopNest, + ImperfectLoopNest, + InvalidLoopStructure, + OuterLoopLowerBoundUnknown + }; + static LoopNestEnum analyzeLoopNestForPerfectNest(const Loop &OuterLoop, + const Loop &InnerLoop, + ScalarEvolution &SE); }; raw_ostream &operator<<(raw_ostream &, const LoopNest &); diff --git a/llvm/include/llvm/Analysis/MLInlineAdvisor.h b/llvm/include/llvm/Analysis/MLInlineAdvisor.h index 54edbb823263..a218561e61c7 100644 --- a/llvm/include/llvm/Analysis/MLInlineAdvisor.h +++ b/llvm/include/llvm/Analysis/MLInlineAdvisor.h @@ -38,6 +38,7 @@ public: bool isForcedToStop() const { return ForceStop; } int64_t getLocalCalls(Function &F); const MLModelRunner &getModelRunner() const { return *ModelRunner.get(); } + void onModuleInvalidated() override { Invalid = true; } protected: std::unique_ptr getAdviceImpl(CallBase &CB) override; @@ -55,6 +56,7 @@ protected: private: int64_t getModuleIRSize() const; + bool Invalid = true; std::unique_ptr CG; int64_t NodeCount = 0; diff --git a/llvm/include/llvm/Analysis/MemorySSA.h b/llvm/include/llvm/Analysis/MemorySSA.h index f40b99968fd3..48aeef371e3d 100644 --- a/llvm/include/llvm/Analysis/MemorySSA.h +++ b/llvm/include/llvm/Analysis/MemorySSA.h @@ -106,9 +106,6 @@ namespace llvm { -/// Enables memory ssa as a dependency for loop passes. -extern cl::opt EnableMSSALoopDependency; - class AllocaInst; class Function; class Instruction; @@ -786,21 +783,22 @@ public: /// dominates Use \p B. bool dominates(const MemoryAccess *A, const Use &B) const; + enum class VerificationLevel { Fast, Full }; /// Verify that MemorySSA is self consistent (IE definitions dominate /// all uses, uses appear in the right places). This is used by unit tests. - void verifyMemorySSA() const; + void verifyMemorySSA(VerificationLevel = VerificationLevel::Fast) const; /// Used in various insertion functions to specify whether we are talking /// about the beginning or end of a block. enum InsertionPlace { Beginning, End, BeforeTerminator }; protected: - // Used by Memory SSA annotater, dumpers, and wrapper pass - friend class MemorySSAAnnotatedWriter; + // Used by Memory SSA dumpers and wrapper pass friend class MemorySSAPrinterLegacyPass; friend class MemorySSAUpdater; - void verifyOrderingDominationAndDefUses(Function &F) const; + void verifyOrderingDominationAndDefUses( + Function &F, VerificationLevel = VerificationLevel::Fast) const; void verifyDominationNumbers(const Function &F) const; void verifyPrevDefInPhis(Function &F) const; @@ -898,6 +896,13 @@ private: unsigned NextID; }; +/// Enables verification of MemorySSA. +/// +/// The checks which this flag enables is exensive and disabled by default +/// unless `EXPENSIVE_CHECKS` is defined. The flag `-verify-memoryssa` can be +/// used to selectively enable the verification without re-compilation. +extern bool VerifyMemorySSA; + // Internal MemorySSA utils, for use by MemorySSA classes and walkers class MemorySSAUtil { protected: @@ -956,6 +961,17 @@ public: PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); }; +/// Printer pass for \c MemorySSA via the walker. +class MemorySSAWalkerPrinterPass + : public PassInfoMixin { + raw_ostream &OS; + +public: + explicit MemorySSAWalkerPrinterPass(raw_ostream &OS) : OS(OS) {} + + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); +}; + /// Verifier pass for \c MemorySSA. struct MemorySSAVerifierPass : PassInfoMixin { PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); diff --git a/llvm/include/llvm/Analysis/ObjCARCAnalysisUtils.h b/llvm/include/llvm/Analysis/ObjCARCAnalysisUtils.h index 62bdade95d96..17062ab907a6 100644 --- a/llvm/include/llvm/Analysis/ObjCARCAnalysisUtils.h +++ b/llvm/include/llvm/Analysis/ObjCARCAnalysisUtils.h @@ -78,14 +78,17 @@ inline const Value *GetUnderlyingObjCPtr(const Value *V) { } /// A wrapper for GetUnderlyingObjCPtr used for results memoization. -inline const Value * -GetUnderlyingObjCPtrCached(const Value *V, - DenseMap &Cache) { - if (auto InCache = Cache.lookup(V)) - return InCache; +inline const Value *GetUnderlyingObjCPtrCached( + const Value *V, + DenseMap> &Cache) { + // The entry is invalid if either value handle is null. + auto InCache = Cache.lookup(V); + if (InCache.first && InCache.second) + return InCache.second; const Value *Computed = GetUnderlyingObjCPtr(V); - Cache[V] = const_cast(Computed); + Cache[V] = + std::make_pair(const_cast(V), const_cast(Computed)); return Computed; } @@ -168,8 +171,8 @@ bool IsPotentialRetainableObjPtr(const Value *Op, AAResults &AA); /// Helper for GetARCInstKind. Determines what kind of construct CS /// is. inline ARCInstKind GetCallSiteClass(const CallBase &CB) { - for (auto I = CB.arg_begin(), E = CB.arg_end(); I != E; ++I) - if (IsPotentialRetainableObjPtr(*I)) + for (const Use &U : CB.args()) + if (IsPotentialRetainableObjPtr(U)) return CB.onlyReadsMemory() ? ARCInstKind::User : ARCInstKind::CallOrUser; return CB.onlyReadsMemory() ? ARCInstKind::None : ARCInstKind::Call; @@ -204,11 +207,10 @@ inline bool IsObjCIdentifiedObject(const Value *V) { return true; StringRef Section = GV->getSection(); - if (Section.find("__message_refs") != StringRef::npos || - Section.find("__objc_classrefs") != StringRef::npos || - Section.find("__objc_superrefs") != StringRef::npos || - Section.find("__objc_methname") != StringRef::npos || - Section.find("__cstring") != StringRef::npos) + if (Section.contains("__message_refs") || + Section.contains("__objc_classrefs") || + Section.contains("__objc_superrefs") || + Section.contains("__objc_methname") || Section.contains("__cstring")) return true; } } diff --git a/llvm/include/llvm/Analysis/ObjCARCUtil.h b/llvm/include/llvm/Analysis/ObjCARCUtil.h index 2566bfbcf61c..362dd6c29992 100644 --- a/llvm/include/llvm/Analysis/ObjCARCUtil.h +++ b/llvm/include/llvm/Analysis/ObjCARCUtil.h @@ -11,9 +11,11 @@ /// //===----------------------------------------------------------------------===// -#ifndef LLVM_IR_OBJCARCUTIL_H -#define LLVM_IR_OBJCARCUTIL_H +#ifndef LLVM_ANALYSIS_OBJCARCUTIL_H +#define LLVM_ANALYSIS_OBJCARCUTIL_H +#include "llvm/Analysis/ObjCARCInstKind.h" +#include "llvm/IR/Function.h" #include "llvm/IR/InstrTypes.h" #include "llvm/IR/LLVMContext.h" @@ -24,13 +26,6 @@ inline const char *getRVMarkerModuleFlagStr() { return "clang.arc.retainAutoreleasedReturnValueMarker"; } -enum AttachedCallOperandBundle : unsigned { RVOB_Retain, RVOB_Claim }; - -inline AttachedCallOperandBundle -getAttachedCallOperandBundleEnum(bool IsRetain) { - return IsRetain ? RVOB_Retain : RVOB_Claim; -} - inline bool hasAttachedCallOpBundle(const CallBase *CB) { // Ignore the bundle if the return type is void. Global optimization passes // can turn the called function's return type to void. That should happen only @@ -43,14 +38,32 @@ inline bool hasAttachedCallOpBundle(const CallBase *CB) { .hasValue(); } -inline bool hasAttachedCallOpBundle(const CallBase *CB, bool IsRetain) { - assert(hasAttachedCallOpBundle(CB) && - "call doesn't have operand bundle clang_arc_attachedcall"); +/// This function returns operand bundle clang_arc_attachedcall's argument, +/// which is the address of the ARC runtime function. +inline Optional getAttachedARCFunction(const CallBase *CB) { auto B = CB->getOperandBundle(LLVMContext::OB_clang_arc_attachedcall); - if (!B.hasValue()) - return false; - return cast(B->Inputs[0])->getZExtValue() == - getAttachedCallOperandBundleEnum(IsRetain); + if (!B.hasValue() || B->Inputs.size() == 0) + return None; + + return cast(B->Inputs[0]); +} + +/// Check whether the function is retainRV/claimRV. +inline bool isRetainOrClaimRV(ARCInstKind Kind) { + return Kind == ARCInstKind::RetainRV || Kind == ARCInstKind::ClaimRV; +} + +/// This function returns the ARCInstKind of the function attached to operand +/// bundle clang_arc_attachedcall. It returns None if the call doesn't have the +/// operand bundle or the operand is null. Otherwise it returns either RetainRV +/// or ClaimRV. +inline ARCInstKind getAttachedARCFunctionKind(const CallBase *CB) { + Optional Fn = getAttachedARCFunction(CB); + if (!Fn.hasValue()) + return ARCInstKind::None; + auto FnClass = GetFunctionClass(*Fn); + assert(isRetainOrClaimRV(FnClass) && "unexpected ARC runtime function"); + return FnClass; } } // end namespace objcarc diff --git a/llvm/include/llvm/Analysis/ProfileSummaryInfo.h b/llvm/include/llvm/Analysis/ProfileSummaryInfo.h index c95404d96f4e..886800d8a0f5 100644 --- a/llvm/include/llvm/Analysis/ProfileSummaryInfo.h +++ b/llvm/include/llvm/Analysis/ProfileSummaryInfo.h @@ -134,9 +134,13 @@ public: bool isColdCount(uint64_t C) const; /// Returns true if count \p C is considered hot with regard to a given /// hot percentile cutoff value. + /// PercentileCutoff is encoded as a 6 digit decimal fixed point number, where + /// the first two digits are the whole part. E.g. 995000 for 99.5 percentile. bool isHotCountNthPercentile(int PercentileCutoff, uint64_t C) const; /// Returns true if count \p C is considered cold with regard to a given /// cold percentile cutoff value. + /// PercentileCutoff is encoded as a 6 digit decimal fixed point number, where + /// the first two digits are the whole part. E.g. 995000 for 99.5 percentile. bool isColdCountNthPercentile(int PercentileCutoff, uint64_t C) const; /// Returns true if BasicBlock \p BB is considered hot. bool isHotBlock(const BasicBlock *BB, BlockFrequencyInfo *BFI) const; @@ -144,10 +148,14 @@ public: bool isColdBlock(const BasicBlock *BB, BlockFrequencyInfo *BFI) const; /// Returns true if BasicBlock \p BB is considered hot with regard to a given /// hot percentile cutoff value. + /// PercentileCutoff is encoded as a 6 digit decimal fixed point number, where + /// the first two digits are the whole part. E.g. 995000 for 99.5 percentile. bool isHotBlockNthPercentile(int PercentileCutoff, const BasicBlock *BB, BlockFrequencyInfo *BFI) const; /// Returns true if BasicBlock \p BB is considered cold with regard to a given /// cold percentile cutoff value. + /// PercentileCutoff is encoded as a 6 digit decimal fixed point number, where + /// the first two digits are the whole part. E.g. 995000 for 99.5 percentile. bool isColdBlockNthPercentile(int PercentileCutoff, const BasicBlock *BB, BlockFrequencyInfo *BFI) const; /// Returns true if the call site \p CB is considered hot. @@ -162,11 +170,11 @@ public: uint64_t getOrCompColdCountThreshold() const; /// Returns HotCountThreshold if set. uint64_t getHotCountThreshold() const { - return HotCountThreshold ? HotCountThreshold.getValue() : 0; + return HotCountThreshold.getValueOr(0); } /// Returns ColdCountThreshold if set. uint64_t getColdCountThreshold() const { - return ColdCountThreshold ? ColdCountThreshold.getValue() : 0; + return ColdCountThreshold.getValueOr(0); } private: diff --git a/llvm/include/llvm/Analysis/ReplayInlineAdvisor.h b/llvm/include/llvm/Analysis/ReplayInlineAdvisor.h index 3018bcc241d8..a0eb9af62205 100644 --- a/llvm/include/llvm/Analysis/ReplayInlineAdvisor.h +++ b/llvm/include/llvm/Analysis/ReplayInlineAdvisor.h @@ -20,6 +20,46 @@ class Function; class Module; class OptimizationRemarkEmitter; +struct CallSiteFormat { + enum class Format : int { + Line, + LineColumn, + LineDiscriminator, + LineColumnDiscriminator + }; + + bool outputColumn() const { + return OutputFormat == Format::LineColumn || + OutputFormat == Format::LineColumnDiscriminator; + } + + bool outputDiscriminator() const { + return OutputFormat == Format::LineDiscriminator || + OutputFormat == Format::LineColumnDiscriminator; + } + + Format OutputFormat; +}; + +/// Replay Inliner Setup +struct ReplayInlinerSettings { + enum class Scope : int { Function, Module }; + enum class Fallback : int { Original, AlwaysInline, NeverInline }; + + StringRef ReplayFile; + Scope ReplayScope; + Fallback ReplayFallback; + CallSiteFormat ReplayFormat; +}; + +/// Get call site location as a string with the given format +std::string formatCallSiteLocation(DebugLoc DLoc, const CallSiteFormat &Format); + +std::unique_ptr getReplayInlineAdvisor( + Module &M, FunctionAnalysisManager &FAM, LLVMContext &Context, + std::unique_ptr OriginalAdvisor, + const ReplayInlinerSettings &ReplaySettings, bool EmitRemarks); + /// Replay inline advisor that uses optimization remarks from inlining of /// previous build to guide current inlining. This is useful for inliner tuning. class ReplayInlineAdvisor : public InlineAdvisor { @@ -27,15 +67,24 @@ public: ReplayInlineAdvisor(Module &M, FunctionAnalysisManager &FAM, LLVMContext &Context, std::unique_ptr OriginalAdvisor, - StringRef RemarksFile, bool EmitRemarks); + const ReplayInlinerSettings &ReplaySettings, + bool EmitRemarks); std::unique_ptr getAdviceImpl(CallBase &CB) override; bool areReplayRemarksLoaded() const { return HasReplayRemarks; } private: - StringSet<> InlineSitesFromRemarks; + bool hasInlineAdvice(Function &F) const { + return (ReplaySettings.ReplayScope == + ReplayInlinerSettings::Scope::Module) || + CallersToReplay.contains(F.getName()); + } std::unique_ptr OriginalAdvisor; bool HasReplayRemarks = false; + const ReplayInlinerSettings ReplaySettings; bool EmitRemarks = false; + + StringMap InlineSitesFromRemarks; + StringSet<> CallersToReplay; }; } // namespace llvm #endif // LLVM_ANALYSIS_REPLAYINLINEADVISOR_H diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h index ae9c73fede96..a2260688e3d6 100644 --- a/llvm/include/llvm/Analysis/ScalarEvolution.h +++ b/llvm/include/llvm/Analysis/ScalarEvolution.h @@ -25,7 +25,6 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/FoldingSet.h" -#include "llvm/ADT/Hashing.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/SetVector.h" @@ -112,6 +111,24 @@ public: /// Note that NUW and NSW are also valid properties of a recurrence, and /// either implies NW. For convenience, NW will be set for a recurrence /// whenever either NUW or NSW are set. + /// + /// We require that the flag on a SCEV apply to the entire scope in which + /// that SCEV is defined. A SCEV's scope is set of locations dominated by + /// a defining location, which is in turn described by the following rules: + /// * A SCEVUnknown is at the point of definition of the Value. + /// * A SCEVConstant is defined at all points. + /// * A SCEVAddRec is defined starting with the header of the associated + /// loop. + /// * All other SCEVs are defined at the earlest point all operands are + /// defined. + /// + /// The above rules describe a maximally hoisted form (without regards to + /// potential control dependence). A SCEV is defined anywhere a + /// corresponding instruction could be defined in said maximally hoisted + /// form. Note that SCEVUDivExpr (currently the only expression type which + /// can trap) can be defined per these rules in regions where it would trap + /// at runtime. A SCEV being defined does not require the existence of any + /// instruction within the defined scope. enum NoWrapFlags { FlagAnyWrap = 0, // No guarantee. FlagNW = (1 << 0), // No self-wrap. @@ -472,6 +489,10 @@ public: clearFlags(SCEV::NoWrapFlags Flags, SCEV::NoWrapFlags OffFlags) { return (SCEV::NoWrapFlags)(Flags & ~OffFlags); } + LLVM_NODISCARD static bool hasFlags(SCEV::NoWrapFlags Flags, + SCEV::NoWrapFlags TestFlags) { + return TestFlags == maskFlags(Flags, TestFlags); + }; ScalarEvolution(Function &F, TargetLibraryInfo &TLI, AssumptionCache &AC, DominatorTree &DT, LoopInfo &LI); @@ -498,13 +519,26 @@ public: // Returns a wider type among {Ty1, Ty2}. Type *getWiderType(Type *Ty1, Type *Ty2) const; + /// Return true if there exists a point in the program at which both + /// A and B could be operands to the same instruction. + /// SCEV expressions are generally assumed to correspond to instructions + /// which could exists in IR. In general, this requires that there exists + /// a use point in the program where all operands dominate the use. + /// + /// Example: + /// loop { + /// if + /// loop { v1 = load @global1; } + /// else + /// loop { v2 = load @global2; } + /// } + /// No SCEV with operand V1, and v2 can exist in this program. + bool instructionCouldExistWitthOperands(const SCEV *A, const SCEV *B); + /// Return true if the SCEV is a scAddRecExpr or it contains /// scAddRecExpr. The result will be cached in HasRecMap. bool containsAddRecurrence(const SCEV *S); - /// Erase Value from ValueExprMap and ExprValueMap. - void eraseValueFromMap(Value *V); - /// Is operation \p BinOp between \p LHS and \p RHS provably does not have /// a signed/unsigned overflow (\p Signed)? bool willNotOverflow(Instruction::BinaryOps BinOp, bool Signed, @@ -516,6 +550,12 @@ public: std::pair getStrengthenedNoWrapFlagsFromBinOp(const OverflowingBinaryOperator *OBO); + /// Notify this ScalarEvolution that \p User directly uses SCEVs in \p Ops. + void registerUser(const SCEV *User, ArrayRef Ops); + + /// Return true if the SCEV expression contains an undef value. + bool containsUndefs(const SCEV *S) const; + /// Return a SCEV expression for the full generality of the specified /// expression. const SCEV *getSCEV(Value *V); @@ -700,6 +740,9 @@ public: /// cases do exist. const SCEV *getPointerBase(const SCEV *V); + /// Compute an expression equivalent to S - getPointerBase(S). + const SCEV *removePointerBase(const SCEV *S); + /// Return a SCEV expression for the specified value at the specified scope /// in the program. The L value specifies a loop nest to evaluate the /// expression at, where null is the top-level or a specified loop is @@ -735,9 +778,13 @@ public: /// Convert from an "exit count" (i.e. "backedge taken count") to a "trip /// count". A "trip count" is the number of times the header of the loop /// will execute if an exit is taken after the specified number of backedges - /// have been taken. (e.g. TripCount = ExitCount + 1) A zero result - /// must be interpreted as a loop having an unknown trip count. - const SCEV *getTripCountFromExitCount(const SCEV *ExitCount); + /// have been taken. (e.g. TripCount = ExitCount + 1). Note that the + /// expression can overflow if ExitCount = UINT_MAX. \p Extend controls + /// how potential overflow is handled. If true, a wider result type is + /// returned. ex: EC = 255 (i8), TC = 256 (i9). If false, result unsigned + /// wraps with 2s-complement semantics. ex: EC = 255 (i8), TC = 0 (i8) + const SCEV *getTripCountFromExitCount(const SCEV *ExitCount, + bool Extend = true); /// Returns the exact trip count of the loop if we can compute it, and /// the result is a small constant. '0' is used to represent an unknown @@ -762,6 +809,13 @@ public: /// Returns 0 if the trip count is unknown or not constant. unsigned getSmallConstantMaxTripCount(const Loop *L); + /// Returns the upper bound of the loop trip count infered from array size. + /// Can not access bytes starting outside the statically allocated size + /// without being immediate UB. + /// Returns SCEVCouldNotCompute if the trip count could not inferred + /// from array accesses. + const SCEV *getConstantMaxTripCountFromArray(const Loop *L); + /// Returns the largest constant divisor of the trip count as a normal /// unsigned value, if possible. This means that the actual trip count is /// always a multiple of the returned value. Returns 1 if the trip count is @@ -988,14 +1042,13 @@ public: /// Test if the given expression is known to satisfy the condition described /// by Pred, LHS, and RHS in the given Context. bool isKnownPredicateAt(ICmpInst::Predicate Pred, const SCEV *LHS, - const SCEV *RHS, const Instruction *Context); + const SCEV *RHS, const Instruction *CtxI); /// Check whether the condition described by Pred, LHS, and RHS is true or /// false in the given \p Context. If we know it, return the evaluation of /// this condition. If neither is proved, return None. Optional evaluatePredicateAt(ICmpInst::Predicate Pred, const SCEV *LHS, - const SCEV *RHS, - const Instruction *Context); + const SCEV *RHS, const Instruction *CtxI); /// Test if the condition described by Pred, LHS, RHS is known to be true on /// every iteration of the loop of the recurrency LHS. @@ -1045,7 +1098,7 @@ public: getLoopInvariantExitCondDuringFirstIterations(ICmpInst::Predicate Pred, const SCEV *LHS, const SCEV *RHS, const Loop *L, - const Instruction *Context, + const Instruction *CtxI, const SCEV *MaxIter); /// Simplify LHS and RHS in a comparison with predicate Pred. Return true @@ -1092,110 +1145,11 @@ public: /// Return the size of an element read or written by Inst. const SCEV *getElementSize(Instruction *Inst); - /// Compute the array dimensions Sizes from the set of Terms extracted from - /// the memory access function of this SCEVAddRecExpr (second step of - /// delinearization). - void findArrayDimensions(SmallVectorImpl &Terms, - SmallVectorImpl &Sizes, - const SCEV *ElementSize); - void print(raw_ostream &OS) const; void verify() const; bool invalidate(Function &F, const PreservedAnalyses &PA, FunctionAnalysisManager::Invalidator &Inv); - /// Collect parametric terms occurring in step expressions (first step of - /// delinearization). - void collectParametricTerms(const SCEV *Expr, - SmallVectorImpl &Terms); - - /// Return in Subscripts the access functions for each dimension in Sizes - /// (third step of delinearization). - void computeAccessFunctions(const SCEV *Expr, - SmallVectorImpl &Subscripts, - SmallVectorImpl &Sizes); - - /// Gathers the individual index expressions from a GEP instruction. - /// - /// This function optimistically assumes the GEP references into a fixed size - /// array. If this is actually true, this function returns a list of array - /// subscript expressions in \p Subscripts and a list of integers describing - /// the size of the individual array dimensions in \p Sizes. Both lists have - /// either equal length or the size list is one element shorter in case there - /// is no known size available for the outermost array dimension. Returns true - /// if successful and false otherwise. - bool getIndexExpressionsFromGEP(const GetElementPtrInst *GEP, - SmallVectorImpl &Subscripts, - SmallVectorImpl &Sizes); - - /// Split this SCEVAddRecExpr into two vectors of SCEVs representing the - /// subscripts and sizes of an array access. - /// - /// The delinearization is a 3 step process: the first two steps compute the - /// sizes of each subscript and the third step computes the access functions - /// for the delinearized array: - /// - /// 1. Find the terms in the step functions - /// 2. Compute the array size - /// 3. Compute the access function: divide the SCEV by the array size - /// starting with the innermost dimensions found in step 2. The Quotient - /// is the SCEV to be divided in the next step of the recursion. The - /// Remainder is the subscript of the innermost dimension. Loop over all - /// array dimensions computed in step 2. - /// - /// To compute a uniform array size for several memory accesses to the same - /// object, one can collect in step 1 all the step terms for all the memory - /// accesses, and compute in step 2 a unique array shape. This guarantees - /// that the array shape will be the same across all memory accesses. - /// - /// FIXME: We could derive the result of steps 1 and 2 from a description of - /// the array shape given in metadata. - /// - /// Example: - /// - /// A[][n][m] - /// - /// for i - /// for j - /// for k - /// A[j+k][2i][5i] = - /// - /// The initial SCEV: - /// - /// A[{{{0,+,2*m+5}_i, +, n*m}_j, +, n*m}_k] - /// - /// 1. Find the different terms in the step functions: - /// -> [2*m, 5, n*m, n*m] - /// - /// 2. Compute the array size: sort and unique them - /// -> [n*m, 2*m, 5] - /// find the GCD of all the terms = 1 - /// divide by the GCD and erase constant terms - /// -> [n*m, 2*m] - /// GCD = m - /// divide by GCD -> [n, 2] - /// remove constant terms - /// -> [n] - /// size of the array is A[unknown][n][m] - /// - /// 3. Compute the access function - /// a. Divide {{{0,+,2*m+5}_i, +, n*m}_j, +, n*m}_k by the innermost size m - /// Quotient: {{{0,+,2}_i, +, n}_j, +, n}_k - /// Remainder: {{{0,+,5}_i, +, 0}_j, +, 0}_k - /// The remainder is the subscript of the innermost array dimension: [5i]. - /// - /// b. Divide Quotient: {{{0,+,2}_i, +, n}_j, +, n}_k by next outer size n - /// Quotient: {{{0,+,0}_i, +, 1}_j, +, 1}_k - /// Remainder: {{{0,+,2}_i, +, 0}_j, +, 0}_k - /// The Remainder is the subscript of the next array dimension: [2i]. - /// - /// The subscript of the outermost dimension is the Quotient: [j+k]. - /// - /// Overall, we have: A[][n][m], and the access function: A[j+k][2i][5i]. - void delinearize(const SCEV *Expr, SmallVectorImpl &Subscripts, - SmallVectorImpl &Sizes, - const SCEV *ElementSize); - /// Return the DataLayout associated with the module this SCEV instance is /// operating on. const DataLayout &getDataLayout() const { @@ -1234,6 +1188,18 @@ public: /// Try to apply information from loop guards for \p L to \p Expr. const SCEV *applyLoopGuards(const SCEV *Expr, const Loop *L); + /// Return true if the loop has no abnormal exits. That is, if the loop + /// is not infinite, it must exit through an explicit edge in the CFG. + /// (As opposed to either a) throwing out of the function or b) entering a + /// well defined infinite loop in some callee.) + bool loopHasNoAbnormalExits(const Loop *L) { + return getLoopProperties(L).HasNoAbnormalExits; + } + + /// Return true if this loop is finite by assumption. That is, + /// to be infinite, it must also be undefined. + bool loopIsFiniteByAssumption(const Loop *L); + private: /// A CallbackVH to arrange for ScalarEvolution to be notified whenever a /// Value is deleted. @@ -1532,15 +1498,15 @@ private: LoopDispositions; struct LoopProperties { - /// Set to true if the loop contains no instruction that can have side - /// effects (i.e. via throwing an exception, volatile or atomic access). - bool HasNoAbnormalExits; - /// Set to true if the loop contains no instruction that can abnormally exit /// the loop (i.e. via throwing an exception, by terminating the thread /// cleanly or by infinite looping in a called function). Strictly /// speaking, the last one is not leaving the loop, but is identical to /// leaving the loop for reasoning about undefined behavior. + bool HasNoAbnormalExits; + + /// Set to true if the loop contains no instruction that can have side + /// effects (i.e. via throwing an exception, volatile or atomic access). bool HasNoSideEffects; }; @@ -1554,14 +1520,6 @@ private: return getLoopProperties(L).HasNoSideEffects; } - bool loopHasNoAbnormalExits(const Loop *L) { - return getLoopProperties(L).HasNoAbnormalExits; - } - - /// Return true if this loop is finite by assumption. That is, - /// to be infinite, it must also be undefined. - bool loopIsFiniteByAssumption(const Loop *L); - /// Compute a LoopDisposition value. LoopDisposition computeLoopDisposition(const SCEV *S, const Loop *L); @@ -1574,6 +1532,9 @@ private: /// Compute a BlockDisposition value. BlockDisposition computeBlockDisposition(const SCEV *S, const BasicBlock *BB); + /// Stores all SCEV that use a given SCEV as its direct operand. + DenseMap > SCEVUsers; + /// Memoized results from getRange DenseMap UnsignedRanges; @@ -1600,22 +1561,22 @@ private: /// copied if its needed for longer. const ConstantRange &getRangeRef(const SCEV *S, RangeSignHint Hint); - /// Determines the range for the affine SCEVAddRecExpr {\p Start,+,\p Stop}. + /// Determines the range for the affine SCEVAddRecExpr {\p Start,+,\p Step}. /// Helper for \c getRange. - ConstantRange getRangeForAffineAR(const SCEV *Start, const SCEV *Stop, + ConstantRange getRangeForAffineAR(const SCEV *Start, const SCEV *Step, const SCEV *MaxBECount, unsigned BitWidth); /// Determines the range for the affine non-self-wrapping SCEVAddRecExpr {\p - /// Start,+,\p Stop}. + /// Start,+,\p Step}. ConstantRange getRangeForAffineNoSelfWrappingAR(const SCEVAddRecExpr *AddRec, const SCEV *MaxBECount, unsigned BitWidth, RangeSignHint SignHint); /// Try to compute a range for the affine SCEVAddRecExpr {\p Start,+,\p - /// Stop} by "factoring out" a ternary expression from the add recurrence. + /// Step} by "factoring out" a ternary expression from the add recurrence. /// Helper called by \c getRange. - ConstantRange getRangeViaFactoring(const SCEV *Start, const SCEV *Stop, + ConstantRange getRangeViaFactoring(const SCEV *Start, const SCEV *Step, const SCEV *MaxBECount, unsigned BitWidth); /// If the unknown expression U corresponds to a simple recurrence, return @@ -1761,12 +1722,6 @@ private: BasicBlock *ExitingBB, bool IsSubExpr); - /// Given an exit condition of 'icmp op load X, cst', try to see if we can - /// compute the backedge-taken count. - ExitLimit computeLoadConstantCompareExitLimit(LoadInst *LI, Constant *RHS, - const Loop *L, - ICmpInst::Predicate p); - /// Compute the exit limit of a loop that is controlled by a /// "(IV >> 1) != 0" type comparison. We cannot compute the exact trip /// count in these cases (since SCEV has no way of expressing them), but we @@ -1839,7 +1794,7 @@ private: const SCEV *RHS, ICmpInst::Predicate FoundPred, const SCEV *FoundLHS, const SCEV *FoundRHS, - const Instruction *Context); + const Instruction *CtxI); /// Test whether the condition described by Pred, LHS, and RHS is true /// whenever the condition described by FoundPred, FoundLHS, FoundRHS is @@ -1914,7 +1869,7 @@ private: const SCEV *LHS, const SCEV *RHS, const SCEV *FoundLHS, const SCEV *FoundRHS, - const Instruction *Context); + const Instruction *CtxI); /// Test whether the condition described by Pred, LHS, and RHS is true /// whenever the condition described by Pred, FoundLHS, and FoundRHS is @@ -1956,12 +1911,18 @@ private: bool splitBinaryAdd(const SCEV *Expr, const SCEV *&L, const SCEV *&R, SCEV::NoWrapFlags &Flags); - /// Drop memoized information computed for S. - void forgetMemoizedResults(const SCEV *S); + /// Drop memoized information for all \p SCEVs. + void forgetMemoizedResults(ArrayRef SCEVs); + + /// Helper for forgetMemoizedResults. + void forgetMemoizedResultsImpl(const SCEV *S); /// Return an existing SCEV for V if there is one, otherwise return nullptr. const SCEV *getExistingSCEV(Value *V); + /// Erase Value from ValueExprMap and ExprValueMap. + void eraseValueFromMap(Value *V); + /// Return false iff given SCEV contains a SCEVUnknown with NULL value- /// pointer. bool checkValidity(const SCEV *S) const; @@ -1995,6 +1956,27 @@ private: /// would trigger undefined behavior on overflow. SCEV::NoWrapFlags getNoWrapFlagsFromUB(const Value *V); + /// Return a scope which provides an upper bound on the defining scope of + /// 'S'. Specifically, return the first instruction in said bounding scope. + /// Return nullptr if the scope is trivial (function entry). + /// (See scope definition rules associated with flag discussion above) + const Instruction *getNonTrivialDefiningScopeBound(const SCEV *S); + + /// Return a scope which provides an upper bound on the defining scope for + /// a SCEV with the operands in Ops. The outparam Precise is set if the + /// bound found is a precise bound (i.e. must be the defining scope.) + const Instruction *getDefiningScopeBound(ArrayRef Ops, + bool &Precise); + + /// Wrapper around the above for cases which don't care if the bound + /// is precise. + const Instruction *getDefiningScopeBound(ArrayRef Ops); + + /// Given two instructions in the same function, return true if we can + /// prove B must execute given A executes. + bool isGuaranteedToTransferExecutionTo(const Instruction *A, + const Instruction *B); + /// Return true if the SCEV corresponding to \p I is never poison. Proving /// this is more complex than proving that just \p I is never poison, since /// SCEV commons expressions across control flow, and you can have cases @@ -2036,8 +2018,11 @@ private: /// permitted by Start, End, and Stride. This is for loops of the form /// {Start, +, Stride} LT End. /// - /// Precondition: the induction variable is known to be positive. We *don't* - /// assert these preconditions so please be careful. + /// Preconditions: + /// * the induction variable is known to be positive. + /// * the induction variable is assumed not to overflow (i.e. either it + /// actually doesn't, or we'd have to immediately execute UB) + /// We *don't* assert these preconditions so please be careful. const SCEV *computeMaxBECountForLT(const SCEV *Start, const SCEV *Stride, const SCEV *End, unsigned BitWidth, bool IsSigned); @@ -2072,31 +2057,20 @@ private: /// an add rec on said loop. void getUsedLoops(const SCEV *S, SmallPtrSetImpl &LoopsUsed); - /// Find all of the loops transitively used in \p S, and update \c LoopUsers - /// accordingly. - void addToLoopUseLists(const SCEV *S); - /// Try to match the pattern generated by getURemExpr(A, B). If successful, /// Assign A and B to LHS and RHS, respectively. bool matchURem(const SCEV *Expr, const SCEV *&LHS, const SCEV *&RHS); /// Look for a SCEV expression with type `SCEVType` and operands `Ops` in - /// `UniqueSCEVs`. - /// - /// The first component of the returned tuple is the SCEV if found and null - /// otherwise. The second component is the `FoldingSetNodeID` that was - /// constructed to look up the SCEV and the third component is the insertion - /// point. - std::tuple - findExistingSCEVInCache(SCEVTypes SCEVType, ArrayRef Ops); + /// `UniqueSCEVs`. Return if found, else nullptr. + SCEV *findExistingSCEVInCache(SCEVTypes SCEVType, ArrayRef Ops); FoldingSet UniqueSCEVs; FoldingSet UniquePreds; BumpPtrAllocator SCEVAllocator; - /// This maps loops to a list of SCEV expressions that (transitively) use said - /// loop. - DenseMap> LoopUsers; + /// This maps loops to a list of addrecs that directly use said loop. + DenseMap> LoopUsers; /// Cache tentative mappings from UnknownSCEVs in a Loop, to a SCEV expression /// they can be rewritten into under certain predicates. diff --git a/llvm/include/llvm/Analysis/StackLifetime.h b/llvm/include/llvm/Analysis/StackLifetime.h index df342a9533ee..239aec4e258b 100644 --- a/llvm/include/llvm/Analysis/StackLifetime.h +++ b/llvm/include/llvm/Analysis/StackLifetime.h @@ -191,6 +191,8 @@ public: StackLifetimePrinterPass(raw_ostream &OS, StackLifetime::LivenessType Type) : Type(Type), OS(OS) {} PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); + void printPipeline(raw_ostream &OS, + function_ref MapClassName2PassName); }; } // end namespace llvm diff --git a/llvm/include/llvm/Analysis/StackSafetyAnalysis.h b/llvm/include/llvm/Analysis/StackSafetyAnalysis.h index 59c1e3e3bd56..751735f3e59f 100644 --- a/llvm/include/llvm/Analysis/StackSafetyAnalysis.h +++ b/llvm/include/llvm/Analysis/StackSafetyAnalysis.h @@ -75,7 +75,15 @@ public: StackSafetyGlobalInfo &operator=(StackSafetyGlobalInfo &&); ~StackSafetyGlobalInfo(); + // Whether we can prove that all accesses to this Alloca are in-range and + // during its lifetime. bool isSafe(const AllocaInst &AI) const; + + // Returns true if the instruction can be proven to do only two types of + // memory accesses: + // (1) live stack locations in-bounds or + // (2) non-stack locations. + bool stackAccessIsSafe(const Instruction &I) const; void print(raw_ostream &O) const; void dump() const; }; diff --git a/llvm/include/llvm/Analysis/TargetLibraryInfo.h b/llvm/include/llvm/Analysis/TargetLibraryInfo.h index 22bfeda0efd0..6e3e1380535e 100644 --- a/llvm/include/llvm/Analysis/TargetLibraryInfo.h +++ b/llvm/include/llvm/Analysis/TargetLibraryInfo.h @@ -76,7 +76,7 @@ class TargetLibraryInfoImpl { /// Return true if the function type FTy is valid for the library function /// F, regardless of whether the function is available. bool isValidProtoForLibFunc(const FunctionType &FTy, LibFunc F, - const DataLayout *DL) const; + const Module &M) const; public: /// List of known vector-functions libraries. @@ -115,6 +115,8 @@ public: /// /// If it is one of the known library functions, return true and set F to the /// corresponding value. + /// + /// FDecl is assumed to have a parent Module when using this function. bool getLibFunc(const Function &FDecl, LibFunc &F) const; /// Forces a function to be marked as unavailable. @@ -238,7 +240,7 @@ public: else { // Disable individual libc/libm calls in TargetLibraryInfo. LibFunc LF; - AttributeSet FnAttrs = (*F)->getAttributes().getFnAttributes(); + AttributeSet FnAttrs = (*F)->getAttributes().getFnAttrs(); for (const Attribute &Attr : FnAttrs) { if (!Attr.isStringAttribute()) continue; diff --git a/llvm/include/llvm/Analysis/TargetTransformInfo.h b/llvm/include/llvm/Analysis/TargetTransformInfo.h index 628058142e48..170d6b8f35ff 100644 --- a/llvm/include/llvm/Analysis/TargetTransformInfo.h +++ b/llvm/include/llvm/Analysis/TargetTransformInfo.h @@ -21,7 +21,6 @@ #ifndef LLVM_ANALYSIS_TARGETTRANSFORMINFO_H #define LLVM_ANALYSIS_TARGETTRANSFORMINFO_H -#include "llvm/Analysis/IVDescriptors.h" #include "llvm/IR/InstrTypes.h" #include "llvm/IR/Operator.h" #include "llvm/IR/PassManager.h" @@ -31,6 +30,7 @@ #include "llvm/Support/DataTypes.h" #include "llvm/Support/InstructionCost.h" #include +#include namespace llvm { @@ -47,12 +47,14 @@ class ExtractElementInst; class Function; class GlobalValue; class InstCombiner; +class OptimizationRemarkEmitter; class IntrinsicInst; class LoadInst; class LoopAccessInfo; class Loop; class LoopInfo; class ProfileSummaryInfo; +class RecurrenceDescriptor; class SCEV; class ScalarEvolution; class StoreInst; @@ -97,7 +99,7 @@ struct HardwareLoopInfo { Loop *L = nullptr; BasicBlock *ExitBlock = nullptr; BranchInst *ExitBranch = nullptr; - const SCEV *TripCount = nullptr; + const SCEV *ExitCount = nullptr; IntegerType *CountType = nullptr; Value *LoopDecrement = nullptr; // Decrement the loop counter by this // value in every iteration. @@ -382,8 +384,15 @@ public: bool isNoopAddrSpaceCast(unsigned FromAS, unsigned ToAS) const; + /// Return true if globals in this address space can have initializers other + /// than `undef`. + bool canHaveNonUndefGlobalInitializerInAddressSpace(unsigned AS) const; + unsigned getAssumedAddrSpace(const Value *V) const; + std::pair + getPredicatedAddrSpace(const Value *V) const; + /// Rewrite intrinsic call \p II such that \p OldV will be replaced with \p /// NewV, which has a different address space. This should happen for every /// operand index that collectFlatAddressOperands returned for the intrinsic. @@ -506,7 +515,8 @@ public: /// transformation. The caller will initialize UP with the current /// target-independent defaults. void getUnrollingPreferences(Loop *L, ScalarEvolution &, - UnrollingPreferences &UP) const; + UnrollingPreferences &UP, + OptimizationRemarkEmitter *ORE) const; /// Query the target whether it would be profitable to convert the given loop /// into a hardware loop. @@ -660,6 +670,9 @@ public: /// Return true if the target supports masked expand load. bool isLegalMaskedExpandLoad(Type *DataType) const; + /// Return true if we should be enabling ordered reductions for the target. + bool enableOrderedReductions() const; + /// Return true if the target has a unified operation to calculate division /// and remainder. If so, the additional implicit multiplication and /// subtraction required to calculate a remainder from division are free. This @@ -907,6 +920,9 @@ public: /// architectural maximum vector length, and None otherwise. Optional getMaxVScale() const; + /// \return the value of vscale to tune the cost model for. + Optional getVScaleForTuning() const; + /// \return True if the vectorization factor should be chosen to /// make the vector of the smallest element type match the size of a /// vector register. For wider element types, this could result in @@ -1094,8 +1110,8 @@ public: /// is using a compare with the specified predicate as condition. When vector /// types are passed, \p VecPred must be used for all lanes. InstructionCost - getCmpSelInstrCost(unsigned Opcode, Type *ValTy, Type *CondTy = nullptr, - CmpInst::Predicate VecPred = CmpInst::BAD_ICMP_PREDICATE, + getCmpSelInstrCost(unsigned Opcode, Type *ValTy, Type *CondTy, + CmpInst::Predicate VecPred, TTI::TargetCostKind CostKind = TTI::TCK_RecipThroughput, const Instruction *I = nullptr) const; @@ -1104,6 +1120,16 @@ public: InstructionCost getVectorInstrCost(unsigned Opcode, Type *Val, unsigned Index = -1) const; + /// \return The cost of replication shuffle of \p VF elements typed \p EltTy + /// \p ReplicationFactor times. + /// + /// For example, the mask for \p ReplicationFactor=3 and \p VF=4 is: + /// <0,0,0,1,1,1,2,2,2,3,3,3> + InstructionCost getReplicationShuffleCost(Type *EltTy, int ReplicationFactor, + int VF, + const APInt &DemandedDstElts, + TTI::TargetCostKind CostKind); + /// \return The cost of Load and Store instructions. InstructionCost getMemoryOpCost(unsigned Opcode, Type *Src, Align Alignment, @@ -1452,13 +1478,18 @@ public: virtual bool collectFlatAddressOperands(SmallVectorImpl &OpIndexes, Intrinsic::ID IID) const = 0; virtual bool isNoopAddrSpaceCast(unsigned FromAS, unsigned ToAS) const = 0; + virtual bool + canHaveNonUndefGlobalInitializerInAddressSpace(unsigned AS) const = 0; virtual unsigned getAssumedAddrSpace(const Value *V) const = 0; + virtual std::pair + getPredicatedAddrSpace(const Value *V) const = 0; virtual Value *rewriteIntrinsicWithAddressSpace(IntrinsicInst *II, Value *OldV, Value *NewV) const = 0; virtual bool isLoweredToCall(const Function *F) = 0; virtual void getUnrollingPreferences(Loop *L, ScalarEvolution &, - UnrollingPreferences &UP) = 0; + UnrollingPreferences &UP, + OptimizationRemarkEmitter *ORE) = 0; virtual void getPeelingPreferences(Loop *L, ScalarEvolution &SE, PeelingPreferences &PP) = 0; virtual bool isHardwareLoopProfitable(Loop *L, ScalarEvolution &SE, @@ -1505,6 +1536,7 @@ public: virtual bool isLegalMaskedGather(Type *DataType, Align Alignment) = 0; virtual bool isLegalMaskedCompressStore(Type *DataType) = 0; virtual bool isLegalMaskedExpandLoad(Type *DataType) = 0; + virtual bool enableOrderedReductions() = 0; virtual bool hasDivRemOp(Type *DataType, bool IsSigned) = 0; virtual bool hasVolatileVariant(Instruction *I, unsigned AddrSpace) = 0; virtual bool prefersVectorizedAddressing() = 0; @@ -1563,6 +1595,7 @@ public: virtual TypeSize getRegisterBitWidth(RegisterKind K) const = 0; virtual unsigned getMinVectorRegisterBitWidth() const = 0; virtual Optional getMaxVScale() const = 0; + virtual Optional getVScaleForTuning() const = 0; virtual bool shouldMaximizeVectorBandwidth() const = 0; virtual ElementCount getMinimumVF(unsigned ElemWidth, bool IsScalable) const = 0; @@ -1623,6 +1656,12 @@ public: const Instruction *I) = 0; virtual InstructionCost getVectorInstrCost(unsigned Opcode, Type *Val, unsigned Index) = 0; + + virtual InstructionCost + getReplicationShuffleCost(Type *EltTy, int ReplicationFactor, int VF, + const APInt &DemandedDstElts, + TTI::TargetCostKind CostKind) = 0; + virtual InstructionCost getMemoryOpCost(unsigned Opcode, Type *Src, Align Alignment, unsigned AddressSpace, @@ -1730,8 +1769,8 @@ public: InstructionCost getGEPCost(Type *PointeeType, const Value *Ptr, ArrayRef Operands, - enum TargetTransformInfo::TargetCostKind CostKind) override { - return Impl.getGEPCost(PointeeType, Ptr, Operands); + TargetTransformInfo::TargetCostKind CostKind) override { + return Impl.getGEPCost(PointeeType, Ptr, Operands, CostKind); } unsigned getInliningThresholdMultiplier() override { return Impl.getInliningThresholdMultiplier(); @@ -1775,10 +1814,20 @@ public: return Impl.isNoopAddrSpaceCast(FromAS, ToAS); } + bool + canHaveNonUndefGlobalInitializerInAddressSpace(unsigned AS) const override { + return Impl.canHaveNonUndefGlobalInitializerInAddressSpace(AS); + } + unsigned getAssumedAddrSpace(const Value *V) const override { return Impl.getAssumedAddrSpace(V); } + std::pair + getPredicatedAddrSpace(const Value *V) const override { + return Impl.getPredicatedAddrSpace(V); + } + Value *rewriteIntrinsicWithAddressSpace(IntrinsicInst *II, Value *OldV, Value *NewV) const override { return Impl.rewriteIntrinsicWithAddressSpace(II, OldV, NewV); @@ -1788,8 +1837,9 @@ public: return Impl.isLoweredToCall(F); } void getUnrollingPreferences(Loop *L, ScalarEvolution &SE, - UnrollingPreferences &UP) override { - return Impl.getUnrollingPreferences(L, SE, UP); + UnrollingPreferences &UP, + OptimizationRemarkEmitter *ORE) override { + return Impl.getUnrollingPreferences(L, SE, UP, ORE); } void getPeelingPreferences(Loop *L, ScalarEvolution &SE, PeelingPreferences &PP) override { @@ -1886,6 +1936,9 @@ public: bool isLegalMaskedExpandLoad(Type *DataType) override { return Impl.isLegalMaskedExpandLoad(DataType); } + bool enableOrderedReductions() override { + return Impl.enableOrderedReductions(); + } bool hasDivRemOp(Type *DataType, bool IsSigned) override { return Impl.hasDivRemOp(DataType, IsSigned); } @@ -2015,6 +2068,9 @@ public: Optional getMaxVScale() const override { return Impl.getMaxVScale(); } + Optional getVScaleForTuning() const override { + return Impl.getVScaleForTuning(); + } bool shouldMaximizeVectorBandwidth() const override { return Impl.shouldMaximizeVectorBandwidth(); } @@ -2115,6 +2171,13 @@ public: unsigned Index) override { return Impl.getVectorInstrCost(Opcode, Val, Index); } + InstructionCost + getReplicationShuffleCost(Type *EltTy, int ReplicationFactor, int VF, + const APInt &DemandedDstElts, + TTI::TargetCostKind CostKind) override { + return Impl.getReplicationShuffleCost(EltTy, ReplicationFactor, VF, + DemandedDstElts, CostKind); + } InstructionCost getMemoryOpCost(unsigned Opcode, Type *Src, Align Alignment, unsigned AddressSpace, TTI::TargetCostKind CostKind, diff --git a/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h b/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h index c07a33c9f155..05ef2495475f 100644 --- a/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h +++ b/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h @@ -24,6 +24,7 @@ #include "llvm/IR/Operator.h" #include "llvm/IR/PatternMatch.h" #include "llvm/IR/Type.h" +#include using namespace llvm::PatternMatch; @@ -47,10 +48,9 @@ public: const DataLayout &getDataLayout() const { return DL; } - InstructionCost - getGEPCost(Type *PointeeType, const Value *Ptr, - ArrayRef Operands, - TTI::TargetCostKind CostKind = TTI::TCK_SizeAndLatency) const { + InstructionCost getGEPCost(Type *PointeeType, const Value *Ptr, + ArrayRef Operands, + TTI::TargetCostKind CostKind) const { // In the basic model, we just assume that all-constant GEPs will be folded // into their uses via addressing modes. for (unsigned Idx = 0, Size = Operands.size(); Idx != Size; ++Idx) @@ -105,9 +105,17 @@ public: } bool isNoopAddrSpaceCast(unsigned, unsigned) const { return false; } + bool canHaveNonUndefGlobalInitializerInAddressSpace(unsigned AS) const { + return AS == 0; + }; unsigned getAssumedAddrSpace(const Value *V) const { return -1; } + std::pair + getPredicatedAddrSpace(const Value *V) const { + return std::make_pair(nullptr, -1); + } + Value *rewriteIntrinsicWithAddressSpace(IntrinsicInst *II, Value *OldV, Value *NewV) const { return nullptr; @@ -187,7 +195,8 @@ public: } void getUnrollingPreferences(Loop *, ScalarEvolution &, - TTI::UnrollingPreferences &) const {} + TTI::UnrollingPreferences &, + OptimizationRemarkEmitter *) const {} void getPeelingPreferences(Loop *, ScalarEvolution &, TTI::PeelingPreferences &) const {} @@ -262,6 +271,8 @@ public: bool isLegalMaskedExpandLoad(Type *DataType) const { return false; } + bool enableOrderedReductions() const { return false; } + bool hasDivRemOp(Type *DataType, bool IsSigned) const { return false; } bool hasVolatileVariant(Instruction *I, unsigned AddrSpace) const { @@ -394,6 +405,7 @@ public: unsigned getMinVectorRegisterBitWidth() const { return 128; } Optional getMaxVScale() const { return None; } + Optional getVScaleForTuning() const { return None; } bool shouldMaximizeVectorBandwidth() const { return false; } @@ -539,6 +551,12 @@ public: return 1; } + unsigned getReplicationShuffleCost(Type *EltTy, int ReplicationFactor, int VF, + const APInt &DemandedDstElts, + TTI::TargetCostKind CostKind) { + return 1; + } + InstructionCost getMemoryOpCost(unsigned Opcode, Type *Src, Align Alignment, unsigned AddressSpace, TTI::TargetCostKind CostKind, @@ -614,7 +632,8 @@ public: return 1; } - unsigned getNumberOfParts(Type *Tp) const { return 0; } + // Assume that we have a register of the right size for the type. + unsigned getNumberOfParts(Type *Tp) const { return 1; } InstructionCost getAddressComputationCost(Type *Tp, ScalarEvolution *, const SCEV *) const { @@ -632,9 +651,10 @@ public: return 1; } - InstructionCost getExtendedAddReductionCost( - bool IsMLA, bool IsUnsigned, Type *ResTy, VectorType *Ty, - TTI::TargetCostKind CostKind = TTI::TCK_RecipThroughput) const { + InstructionCost + getExtendedAddReductionCost(bool IsMLA, bool IsUnsigned, Type *ResTy, + VectorType *Ty, + TTI::TargetCostKind CostKind) const { return 1; } @@ -856,10 +876,9 @@ protected: public: using BaseT::getGEPCost; - InstructionCost - getGEPCost(Type *PointeeType, const Value *Ptr, - ArrayRef Operands, - TTI::TargetCostKind CostKind = TTI::TCK_SizeAndLatency) { + InstructionCost getGEPCost(Type *PointeeType, const Value *Ptr, + ArrayRef Operands, + TTI::TargetCostKind CostKind) { assert(PointeeType && Ptr && "can't get GEPCost of nullptr"); assert(cast(Ptr->getType()->getScalarType()) ->isOpaqueOrPointeeTypeMatches(PointeeType) && @@ -964,10 +983,10 @@ public: return TTI::TCC_Free; break; case Instruction::GetElementPtr: { - const GEPOperator *GEP = cast(U); + const auto *GEP = cast(U); return TargetTTI->getGEPCost(GEP->getSourceElementType(), GEP->getPointerOperand(), - Operands.drop_front()); + Operands.drop_front(), CostKind); } case Instruction::Add: case Instruction::FAdd: @@ -1063,58 +1082,94 @@ public: auto *IE = dyn_cast(U); if (!IE) return TTI::TCC_Basic; // FIXME - auto *CI = dyn_cast(IE->getOperand(2)); - unsigned Idx = CI ? CI->getZExtValue() : -1; + unsigned Idx = -1; + if (auto *CI = dyn_cast(IE->getOperand(2))) + if (CI->getValue().getActiveBits() <= 32) + Idx = CI->getZExtValue(); return TargetTTI->getVectorInstrCost(Opcode, Ty, Idx); } case Instruction::ShuffleVector: { auto *Shuffle = dyn_cast(U); if (!Shuffle) return TTI::TCC_Basic; // FIXME + auto *VecTy = cast(U->getType()); auto *VecSrcTy = cast(U->getOperand(0)->getType()); + int NumSubElts, SubIndex; + + if (Shuffle->changesLength()) { + // Treat a 'subvector widening' as a free shuffle. + if (Shuffle->increasesLength() && Shuffle->isIdentityWithPadding()) + return 0; + + if (Shuffle->isExtractSubvectorMask(SubIndex)) + return TargetTTI->getShuffleCost(TTI::SK_ExtractSubvector, VecSrcTy, + Shuffle->getShuffleMask(), SubIndex, + VecTy); + + if (Shuffle->isInsertSubvectorMask(NumSubElts, SubIndex)) + return TargetTTI->getShuffleCost( + TTI::SK_InsertSubvector, VecTy, Shuffle->getShuffleMask(), + SubIndex, + FixedVectorType::get(VecTy->getScalarType(), NumSubElts)); + + int ReplicationFactor, VF; + if (Shuffle->isReplicationMask(ReplicationFactor, VF)) { + APInt DemandedDstElts = + APInt::getNullValue(Shuffle->getShuffleMask().size()); + for (auto I : enumerate(Shuffle->getShuffleMask())) { + if (I.value() != UndefMaskElem) + DemandedDstElts.setBit(I.index()); + } + return TargetTTI->getReplicationShuffleCost( + VecSrcTy->getElementType(), ReplicationFactor, VF, + DemandedDstElts, CostKind); + } - // TODO: Identify and add costs for insert subvector, etc. - int SubIndex; - if (Shuffle->isExtractSubvectorMask(SubIndex)) - return TargetTTI->getShuffleCost(TTI::SK_ExtractSubvector, VecSrcTy, - Shuffle->getShuffleMask(), SubIndex, - VecTy); - else if (Shuffle->changesLength()) return CostKind == TTI::TCK_RecipThroughput ? -1 : 1; - else if (Shuffle->isIdentity()) + } + + if (Shuffle->isIdentity()) return 0; - else if (Shuffle->isReverse()) + + if (Shuffle->isReverse()) return TargetTTI->getShuffleCost(TTI::SK_Reverse, VecTy, Shuffle->getShuffleMask(), 0, nullptr); - else if (Shuffle->isSelect()) + + if (Shuffle->isSelect()) return TargetTTI->getShuffleCost(TTI::SK_Select, VecTy, Shuffle->getShuffleMask(), 0, nullptr); - else if (Shuffle->isTranspose()) + + if (Shuffle->isTranspose()) return TargetTTI->getShuffleCost(TTI::SK_Transpose, VecTy, Shuffle->getShuffleMask(), 0, nullptr); - else if (Shuffle->isZeroEltSplat()) + + if (Shuffle->isZeroEltSplat()) return TargetTTI->getShuffleCost(TTI::SK_Broadcast, VecTy, Shuffle->getShuffleMask(), 0, nullptr); - else if (Shuffle->isSingleSource()) + + if (Shuffle->isSingleSource()) return TargetTTI->getShuffleCost(TTI::SK_PermuteSingleSrc, VecTy, Shuffle->getShuffleMask(), 0, nullptr); + if (Shuffle->isInsertSubvectorMask(NumSubElts, SubIndex)) + return TargetTTI->getShuffleCost( + TTI::SK_InsertSubvector, VecTy, Shuffle->getShuffleMask(), SubIndex, + FixedVectorType::get(VecTy->getScalarType(), NumSubElts)); + return TargetTTI->getShuffleCost(TTI::SK_PermuteTwoSrc, VecTy, Shuffle->getShuffleMask(), 0, nullptr); } case Instruction::ExtractElement: { - unsigned Idx = -1; auto *EEI = dyn_cast(U); if (!EEI) return TTI::TCC_Basic; // FIXME - - auto *CI = dyn_cast(EEI->getOperand(1)); - if (CI) - Idx = CI->getZExtValue(); - - return TargetTTI->getVectorInstrCost(Opcode, U->getOperand(0)->getType(), - Idx); + unsigned Idx = -1; + if (auto *CI = dyn_cast(EEI->getOperand(1))) + if (CI->getValue().getActiveBits() <= 32) + Idx = CI->getZExtValue(); + Type *DstTy = U->getOperand(0)->getType(); + return TargetTTI->getVectorInstrCost(Opcode, DstTy, Idx); } } // By default, just classify everything as 'basic'. diff --git a/llvm/include/llvm/Analysis/TypeMetadataUtils.h b/llvm/include/llvm/Analysis/TypeMetadataUtils.h index 3f7603142900..074c40942b06 100644 --- a/llvm/include/llvm/Analysis/TypeMetadataUtils.h +++ b/llvm/include/llvm/Analysis/TypeMetadataUtils.h @@ -22,6 +22,7 @@ namespace llvm { class CallBase; class CallInst; class Constant; +class Function; class DominatorTree; class Instruction; class Module; @@ -56,7 +57,30 @@ void findDevirtualizableCallsForTypeCheckedLoad( SmallVectorImpl &Preds, bool &HasNonCallUses, const CallInst *CI, DominatorTree &DT); -Constant *getPointerAtOffset(Constant *I, uint64_t Offset, Module &M); -} +/// Processes a Constant recursively looking into elements of arrays, structs +/// and expressions to find a trivial pointer element that is located at the +/// given offset (relative to the beginning of the whole outer Constant). +/// +/// Used for example from GlobalDCE to find an entry in a C++ vtable that +/// matches a vcall offset. +/// +/// To support Swift vtables, getPointerAtOffset can see through "relative +/// pointers", i.e. (sub-)expressions of the form of: +/// +/// @symbol = ... { +/// i32 trunc (i64 sub ( +/// i64 ptrtoint ( @target to i64), i64 ptrtoint (... @symbol to i64) +/// ) to i32) +/// } +/// +/// For such (sub-)expressions, getPointerAtOffset returns the @target pointer. +Constant *getPointerAtOffset(Constant *I, uint64_t Offset, Module &M, + Constant *TopLevelGlobal = nullptr); + +/// Finds the same "relative pointer" pattern as described above, where the +/// target is `F`, and replaces the entire pattern with a constant zero. +void replaceRelativePointerUsersWithZero(Function *F); + +} // namespace llvm #endif diff --git a/llvm/include/llvm/Analysis/Utils/TFUtils.h b/llvm/include/llvm/Analysis/Utils/TFUtils.h index 47ee23e06000..1f6be0e60eb9 100644 --- a/llvm/include/llvm/Analysis/Utils/TFUtils.h +++ b/llvm/include/llvm/Analysis/Utils/TFUtils.h @@ -104,6 +104,9 @@ Optional getTensorSpecFromJSON(LLVMContext &Ctx, struct LoggedFeatureSpec { TensorSpec Spec; Optional LoggingName; + const std::string &getLoggingName() const { + return LoggingName ? *LoggingName : Spec.name(); + } }; /// Load the output specs. If SpecFileOverride is not empty, that path is used. @@ -170,7 +173,9 @@ public: // we can consider using bytes. char *addEntryAndGetFloatOrInt64Buffer(size_t FeatureID); - void print(raw_ostream &OS); + // Flush the content of the log to the stream, clearing the stored data in the + // process. + void flush(raw_ostream &OS); private: std::vector FeatureSpecs; diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h index 90ec742f18e6..b4f38a3e976f 100644 --- a/llvm/include/llvm/Analysis/ValueTracking.h +++ b/llvm/include/llvm/Analysis/ValueTracking.h @@ -203,6 +203,15 @@ constexpr unsigned MaxAnalysisRecursionDepth = 6; const DominatorTree *DT = nullptr, bool UseInstrInfo = true); + /// Get the minimum bit size for this Value \p Op as a signed integer. + /// i.e. x == sext(trunc(x to MinSignedBits) to bitwidth(x)). + /// Similar to the APInt::getMinSignedBits function. + unsigned ComputeMinSignedBits(const Value *Op, const DataLayout &DL, + unsigned Depth = 0, + AssumptionCache *AC = nullptr, + const Instruction *CxtI = nullptr, + const DominatorTree *DT = nullptr); + /// This function computes the integer multiple of Base that equals V. If /// successful, it returns true and returns the multiple in Multiple. If /// unsuccessful, it returns false. Also, if V can be simplified to an @@ -549,6 +558,7 @@ constexpr unsigned MaxAnalysisRecursionDepth = 6; ConstantRange computeConstantRange(const Value *V, bool UseInstrInfo = true, AssumptionCache *AC = nullptr, const Instruction *CtxI = nullptr, + const DominatorTree *DT = nullptr, unsigned Depth = 0); /// Return true if this function can prove that the instruction I will @@ -573,6 +583,18 @@ constexpr unsigned MaxAnalysisRecursionDepth = 6; /// instruction variant of this function. bool isGuaranteedToTransferExecutionToSuccessor(const BasicBlock *BB); + /// Return true if every instruction in the range (Begin, End) is + /// guaranteed to transfer execution to its static successor. \p ScanLimit + /// bounds the search to avoid scanning huge blocks. + bool isGuaranteedToTransferExecutionToSuccessor( + BasicBlock::const_iterator Begin, BasicBlock::const_iterator End, + unsigned ScanLimit = 32); + + /// Same as previous, but with range expressed via iterator_range. + bool isGuaranteedToTransferExecutionToSuccessor( + iterator_range Range, + unsigned ScanLimit = 32); + /// Return true if this function can prove that the instruction I /// is executed for every iteration of the loop L. /// @@ -624,10 +646,16 @@ constexpr unsigned MaxAnalysisRecursionDepth = 6; /// true. If Op raises immediate UB but never creates poison or undef /// (e.g. sdiv I, 0), canCreatePoison returns false. /// + /// \p ConsiderFlags controls whether poison producing flags on the + /// instruction are considered. This can be used to see if the instruction + /// could still introduce undef or poison even without poison generating flags + /// which might be on the instruction. (i.e. could the result of + /// Op->dropPoisonGeneratingFlags() still create poison or undef) + /// /// canCreatePoison returns true if Op can create poison from non-poison /// operands. - bool canCreateUndefOrPoison(const Operator *Op); - bool canCreatePoison(const Operator *Op); + bool canCreateUndefOrPoison(const Operator *Op, bool ConsiderFlags = true); + bool canCreatePoison(const Operator *Op, bool ConsiderFlags = true); /// Return true if V is poison given that ValAssumedPoison is already poison. /// For example, if ValAssumedPoison is `icmp X, 10` and V is `icmp X, 5`, @@ -744,6 +772,10 @@ constexpr unsigned MaxAnalysisRecursionDepth = 6; /// minimum/maximum flavor. CmpInst::Predicate getInverseMinMaxPred(SelectPatternFlavor SPF); + /// Return the minimum or maximum constant value for the specified integer + /// min/max flavor and type. + APInt getMinMaxLimit(SelectPatternFlavor SPF, unsigned BitWidth); + /// Check if the values in \p VL are select instructions that can be converted /// to a min or max (vector) intrinsic. Returns the intrinsic ID, if such a /// conversion is possible, together with a bool indicating whether all select diff --git a/llvm/include/llvm/Analysis/VectorUtils.h b/llvm/include/llvm/Analysis/VectorUtils.h index c890216c9e01..24e2318de48b 100644 --- a/llvm/include/llvm/Analysis/VectorUtils.h +++ b/llvm/include/llvm/Analysis/VectorUtils.h @@ -533,6 +533,12 @@ llvm::SmallVector createStrideMask(unsigned Start, unsigned Stride, llvm::SmallVector createSequentialMask(unsigned Start, unsigned NumInts, unsigned NumUndefs); +/// Given a shuffle mask for a binary shuffle, create the equivalent shuffle +/// mask assuming both operands are identical. This assumes that the unary +/// shuffle will use elements from operand 0 (operand 1 will be unused). +llvm::SmallVector createUnaryMask(ArrayRef Mask, + unsigned NumElts); + /// Concatenate a list of vectors. /// /// This function generates code that concatenate the vectors in \p Vecs into a @@ -686,10 +692,8 @@ public: if (getMember(getFactor() - 1)) return false; - // We have a group with gaps. It therefore cannot be a group of stores, - // and it can't be a reversed access, because such groups get invalidated. - assert(!getMember(0)->mayWriteToMemory() && - "Group should have been invalidated"); + // We have a group with gaps. It therefore can't be a reversed access, + // because such groups get invalidated (TODO). assert(!isReverse() && "Group should have been invalidated"); // This is a group of loads, with gaps, and without a last-member diff --git a/llvm/include/llvm/AsmParser/LLLexer.h b/llvm/include/llvm/AsmParser/LLLexer.h index c97d9781c33b..c30165e4a97b 100644 --- a/llvm/include/llvm/AsmParser/LLLexer.h +++ b/llvm/include/llvm/AsmParser/LLLexer.h @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_LIB_ASMPARSER_LLLEXER_H -#define LLVM_LIB_ASMPARSER_LLLEXER_H +#ifndef LLVM_ASMPARSER_LLLEXER_H +#define LLVM_ASMPARSER_LLLEXER_H #include "LLToken.h" #include "llvm/ADT/APFloat.h" diff --git a/llvm/include/llvm/AsmParser/LLParser.h b/llvm/include/llvm/AsmParser/LLParser.h index 70db9218fa3d..d621c232378c 100644 --- a/llvm/include/llvm/AsmParser/LLParser.h +++ b/llvm/include/llvm/AsmParser/LLParser.h @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_LIB_ASMPARSER_LLPARSER_H -#define LLVM_LIB_ASMPARSER_LLPARSER_H +#ifndef LLVM_ASMPARSER_LLPARSER_H +#define LLVM_ASMPARSER_LLPARSER_H #include "LLLexer.h" #include "llvm/ADT/Optional.h" @@ -172,9 +172,8 @@ namespace llvm { /// getGlobalVal - Get a value with the specified name or ID, creating a /// forward reference record if needed. This can return null if the value /// exists but does not have the right type. - GlobalValue *getGlobalVal(const std::string &N, Type *Ty, LocTy Loc, - bool IsCall); - GlobalValue *getGlobalVal(unsigned ID, Type *Ty, LocTy Loc, bool IsCall); + GlobalValue *getGlobalVal(const std::string &N, Type *Ty, LocTy Loc); + GlobalValue *getGlobalVal(unsigned ID, Type *Ty, LocTy Loc); /// Get a Comdat with the specified name, creating a forward reference /// record if needed. @@ -270,7 +269,6 @@ namespace llvm { bool parseOptionalCommaAlign(MaybeAlign &Alignment, bool &AteExtraComma); bool parseOptionalCommaAddrSpace(unsigned &AddrSpace, LocTy &Loc, bool &AteExtraComma); - bool parseOptionalCommaInAlloca(bool &IsInAlloca); bool parseAllocSizeArguments(unsigned &BaseSizeArg, Optional &HowManyArg); bool parseVScaleRangeArguments(unsigned &MinValue, unsigned &MaxValue); @@ -306,11 +304,10 @@ namespace llvm { unsigned DLLStorageClass, bool DSOLocal, GlobalVariable::ThreadLocalMode TLM, GlobalVariable::UnnamedAddr UnnamedAddr); - bool parseIndirectSymbol(const std::string &Name, LocTy NameLoc, - unsigned L, unsigned Visibility, - unsigned DLLStorageClass, bool DSOLocal, - GlobalVariable::ThreadLocalMode TLM, - GlobalVariable::UnnamedAddr UnnamedAddr); + bool parseAliasOrIFunc(const std::string &Name, LocTy NameLoc, unsigned L, + unsigned Visibility, unsigned DLLStorageClass, + bool DSOLocal, GlobalVariable::ThreadLocalMode TLM, + GlobalVariable::UnnamedAddr UnnamedAddr); bool parseComdat(); bool parseStandaloneMetadata(); bool parseNamedMetadata(); @@ -424,8 +421,8 @@ namespace llvm { /// GetVal - Get a value with the specified name or ID, creating a /// forward reference record if needed. This can return null if the value /// exists but does not have the right type. - Value *getVal(const std::string &Name, Type *Ty, LocTy Loc, bool IsCall); - Value *getVal(unsigned ID, Type *Ty, LocTy Loc, bool IsCall); + Value *getVal(const std::string &Name, Type *Ty, LocTy Loc); + Value *getVal(unsigned ID, Type *Ty, LocTy Loc); /// setInstName - After an instruction is parsed and inserted into its /// basic block, this installs its name. @@ -447,10 +444,10 @@ namespace llvm { }; bool convertValIDToValue(Type *Ty, ValID &ID, Value *&V, - PerFunctionState *PFS, bool IsCall); + PerFunctionState *PFS); Value *checkValidVariableType(LocTy Loc, const Twine &Name, Type *Ty, - Value *Val, bool IsCall); + Value *Val); bool parseConstantValue(Type *Ty, Constant *&C); bool parseValue(Type *Ty, Value *&V, PerFunctionState *PFS); diff --git a/llvm/include/llvm/AsmParser/LLToken.h b/llvm/include/llvm/AsmParser/LLToken.h index aa49c68fe924..f8ca054863ac 100644 --- a/llvm/include/llvm/AsmParser/LLToken.h +++ b/llvm/include/llvm/AsmParser/LLToken.h @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_LIB_ASMPARSER_LLTOKEN_H -#define LLVM_LIB_ASMPARSER_LLTOKEN_H +#ifndef LLVM_ASMPARSER_LLTOKEN_H +#define LLVM_ASMPARSER_LLTOKEN_H namespace llvm { namespace lltok { @@ -190,6 +190,7 @@ enum Kind { kw_convergent, kw_dereferenceable, kw_dereferenceable_or_null, + kw_disable_sanitizer_instrumentation, kw_elementtype, kw_inaccessiblememonly, kw_inaccessiblemem_or_argmemonly, @@ -403,6 +404,9 @@ enum Kind { kw_returnDoesNotAlias, kw_noInline, kw_alwaysInline, + kw_noUnwind, + kw_mayThrow, + kw_hasUnknownCall, kw_calls, kw_callee, kw_params, diff --git a/llvm/include/llvm/BinaryFormat/Dwarf.def b/llvm/include/llvm/BinaryFormat/Dwarf.def index 34f124b5779a..61f3f27ebb47 100644 --- a/llvm/include/llvm/BinaryFormat/Dwarf.def +++ b/llvm/include/llvm/BinaryFormat/Dwarf.def @@ -248,6 +248,9 @@ HANDLE_DW_TAG(0x5103, ALTIUM_rev_carry_type, 0, ALTIUM, DW_KIND_NONE) // M16 __rom qualifier HANDLE_DW_TAG(0x5111, ALTIUM_rom, 0, ALTIUM, DW_KIND_NONE) +// LLVM +HANDLE_DW_TAG(0x6000, LLVM_annotation, 0, LLVM, DW_KIND_NONE) + // Green Hills. HANDLE_DW_TAG(0x8004, GHS_namespace, 0, GHS, DW_KIND_NONE) HANDLE_DW_TAG(0x8005, GHS_using_namespace, 0, GHS, DW_KIND_NONE) diff --git a/llvm/include/llvm/BinaryFormat/DynamicTags.def b/llvm/include/llvm/BinaryFormat/DynamicTags.def index c08f8a53bdb5..814d8b113ec4 100644 --- a/llvm/include/llvm/BinaryFormat/DynamicTags.def +++ b/llvm/include/llvm/BinaryFormat/DynamicTags.def @@ -31,6 +31,11 @@ #define PPC64_DYNAMIC_TAG_DEFINED #endif +#ifndef RISCV_DYNAMIC_TAG +#define RISCV_DYNAMIC_TAG(name, value) DYNAMIC_TAG(name, value) +#define RISCV_DYNAMIC_TAG_DEFINED +#endif + #ifndef DYNAMIC_TAG_MARKER #define DYNAMIC_TAG_MARKER(name, value) DYNAMIC_TAG(name, value) #define DYNAMIC_TAG_MARKER_DEFINED @@ -213,6 +218,9 @@ PPC_DYNAMIC_TAG(PPC_OPT, 0x70000001) // Has TLS optimization. PPC64_DYNAMIC_TAG(PPC64_GLINK, 0x70000000) // Address of 32 bytes before the // first glink lazy resolver stub. +// RISC-V specific dynamic array tags. +RISCV_DYNAMIC_TAG(RISCV_VARIANT_CC, 0x70000001) + // Sun machine-independent extensions. DYNAMIC_TAG(AUXILIARY, 0x7FFFFFFD) // Shared object to load before self DYNAMIC_TAG(USED, 0x7FFFFFFE) // Same as DT_NEEDED @@ -243,3 +251,7 @@ DYNAMIC_TAG(FILTER, 0x7FFFFFFF) // Shared object to get values from #undef PPC64_DYNAMIC_TAG #undef PPC64_DYNAMIC_TAG_DEFINED #endif +#ifdef RISCV_DYNAMIC_TAG_DEFINED +#undef RISCV_DYNAMIC_TAG +#undef RISCV_DYNAMIC_TAG_DEFINED +#endif diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h index 6148f968cdba..a270fd399aeb 100644 --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -660,6 +660,12 @@ enum { #include "ELFRelocs/RISCV.def" }; +enum { + // Symbol may follow different calling convention than the standard calling + // convention. + STO_RISCV_VARIANT_CC = 0x80 +}; + // ELF Relocation types for S390/zSeries enum { #include "ELFRelocs/SystemZ.def" @@ -1596,6 +1602,16 @@ enum { NT_FREEBSD_PROCSTAT_AUXV = 16, }; +// OpenBSD core note types. +enum { + NT_OPENBSD_PROCINFO = 10, + NT_OPENBSD_AUXV = 11, + NT_OPENBSD_REGS = 20, + NT_OPENBSD_FPREGS = 21, + NT_OPENBSD_XFPREGS = 22, + NT_OPENBSD_WCOOKIE = 23, +}; + // AMDGPU-specific section indices. enum { SHN_AMDGPU_LDS = 0xff00, // Variable in LDS; symbol encoded like SHN_COMMON @@ -1618,6 +1634,13 @@ enum { NT_AMDGPU_METADATA = 32 }; +// LLVMOMPOFFLOAD specific notes. +enum : unsigned { + NT_LLVM_OPENMP_OFFLOAD_VERSION = 1, + NT_LLVM_OPENMP_OFFLOAD_PRODUCER = 2, + NT_LLVM_OPENMP_OFFLOAD_PRODUCER_VERSION = 3 +}; + enum { GNU_ABI_TAG_LINUX = 0, GNU_ABI_TAG_HURD = 1, diff --git a/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def b/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def index 9f2f0540bcbd..454450950444 100644 --- a/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def +++ b/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def @@ -46,10 +46,6 @@ ELF_RELOC(R_RISCV_ALIGN, 43) ELF_RELOC(R_RISCV_RVC_BRANCH, 44) ELF_RELOC(R_RISCV_RVC_JUMP, 45) ELF_RELOC(R_RISCV_RVC_LUI, 46) -ELF_RELOC(R_RISCV_GPREL_I, 47) -ELF_RELOC(R_RISCV_GPREL_S, 48) -ELF_RELOC(R_RISCV_TPREL_I, 49) -ELF_RELOC(R_RISCV_TPREL_S, 50) ELF_RELOC(R_RISCV_RELAX, 51) ELF_RELOC(R_RISCV_SUB6, 52) ELF_RELOC(R_RISCV_SET6, 53) diff --git a/llvm/include/llvm/BinaryFormat/MachO.def b/llvm/include/llvm/BinaryFormat/MachO.def index 76dcc58ba048..f68ecefa6c9e 100644 --- a/llvm/include/llvm/BinaryFormat/MachO.def +++ b/llvm/include/llvm/BinaryFormat/MachO.def @@ -74,6 +74,8 @@ HANDLE_LOAD_COMMAND(LC_VERSION_MIN_TVOS, 0x0000002Fu, version_min_command) HANDLE_LOAD_COMMAND(LC_VERSION_MIN_WATCHOS, 0x00000030u, version_min_command) HANDLE_LOAD_COMMAND(LC_NOTE, 0x00000031u, note_command) HANDLE_LOAD_COMMAND(LC_BUILD_VERSION, 0x00000032u, build_version_command) +HANDLE_LOAD_COMMAND(LC_DYLD_EXPORTS_TRIE, 0x80000033u, linkedit_data_command) +HANDLE_LOAD_COMMAND(LC_DYLD_CHAINED_FIXUPS, 0x80000034u, linkedit_data_command) #endif diff --git a/llvm/include/llvm/BinaryFormat/Wasm.h b/llvm/include/llvm/BinaryFormat/Wasm.h index c38e64928521..0bc8c4e167d8 100644 --- a/llvm/include/llvm/BinaryFormat/Wasm.h +++ b/llvm/include/llvm/BinaryFormat/Wasm.h @@ -7,7 +7,7 @@ //===----------------------------------------------------------------------===// // // This file defines manifest constants for the wasm object file format. -// See: https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md +// See: https://github.com/WebAssembly/design/blob/main/BinaryEncoding.md // //===----------------------------------------------------------------------===// @@ -36,12 +36,25 @@ struct WasmObjectHeader { uint32_t Version; }; +struct WasmDylinkImportInfo { + StringRef Module; + StringRef Field; + uint32_t Flags; +}; + +struct WasmDylinkExportInfo { + StringRef Name; + uint32_t Flags; +}; + struct WasmDylinkInfo { uint32_t MemorySize; // Memory size in bytes uint32_t MemoryAlignment; // P2 alignment of memory uint32_t TableSize; // Table size in elements uint32_t TableAlignment; // P2 alignment of table std::vector Needed; // Shared library dependencies + std::vector ImportInfo; + std::vector ExportInfo; }; struct WasmProducerInfo { @@ -101,15 +114,9 @@ struct WasmGlobal { StringRef SymbolName; // from the "linking" section }; -struct WasmTagType { - // Kind of tag. Currently only WASM_TAG_ATTRIBUTE_EXCEPTION is possible. - uint8_t Attribute; - uint32_t SigIndex; -}; - struct WasmTag { uint32_t Index; - WasmTagType Type; + uint32_t SigIndex; StringRef SymbolName; // from the "linking" section }; @@ -122,7 +129,6 @@ struct WasmImport { WasmGlobalType Global; WasmTableType Table; WasmLimits Memory; - WasmTagType Tag; }; }; @@ -133,6 +139,7 @@ struct WasmLocalDecl { struct WasmFunction { uint32_t Index; + uint32_t SigIndex; std::vector Locals; ArrayRef Body; uint32_t CodeSectionOffset; @@ -284,11 +291,14 @@ enum : unsigned { // Opcodes used in synthetic functions. enum : unsigned { - WASM_OPCODE_IF = 0x04, - WASM_OPCODE_ELSE = 0x05, + WASM_OPCODE_BLOCK = 0x02, + WASM_OPCODE_BR = 0x0c, + WASM_OPCODE_BR_TABLE = 0x0e, + WASM_OPCODE_RETURN = 0x0f, WASM_OPCODE_DROP = 0x1a, WASM_OPCODE_MISC_PREFIX = 0xfc, WASM_OPCODE_MEMORY_INIT = 0x08, + WASM_OPCODE_MEMORY_FILL = 0x0b, WASM_OPCODE_DATA_DROP = 0x09, WASM_OPCODE_ATOMICS_PREFIX = 0xfe, WASM_OPCODE_ATOMIC_NOTIFY = 0x00, @@ -339,6 +349,14 @@ enum : unsigned { WASM_SYMBOL_TABLE = 0x8, }; +// Kind codes used in the custom "dylink" section +enum : unsigned { + WASM_DYLINK_MEM_INFO = 0x1, + WASM_DYLINK_NEEDED = 0x2, + WASM_DYLINK_EXPORT_INFO = 0x3, + WASM_DYLINK_IMPORT_INFO = 0x4, +}; + // Kind codes used in the custom "linking" section in the WASM_COMDAT_INFO enum : unsigned { WASM_COMDAT_DATA = 0x0, @@ -379,6 +397,7 @@ const unsigned WASM_SYMBOL_UNDEFINED = 0x10; const unsigned WASM_SYMBOL_EXPORTED = 0x20; const unsigned WASM_SYMBOL_EXPLICIT_NAME = 0x40; const unsigned WASM_SYMBOL_NO_STRIP = 0x80; +const unsigned WASM_SYMBOL_TLS = 0x100; #define WASM_RELOC(name, value) name = value, diff --git a/llvm/include/llvm/BinaryFormat/WasmTraits.h b/llvm/include/llvm/BinaryFormat/WasmTraits.h index 930ee690bcc0..bef9dd3291ca 100644 --- a/llvm/include/llvm/BinaryFormat/WasmTraits.h +++ b/llvm/include/llvm/BinaryFormat/WasmTraits.h @@ -18,10 +18,8 @@ namespace llvm { -template struct DenseMapInfo; - // Traits for using WasmSignature in a DenseMap. -template <> struct DenseMapInfo { +template <> struct DenseMapInfo { static wasm::WasmSignature getEmptyKey() { wasm::WasmSignature Sig; Sig.State = wasm::WasmSignature::Empty; @@ -47,7 +45,7 @@ template <> struct DenseMapInfo { }; // Traits for using WasmGlobalType in a DenseMap -template <> struct DenseMapInfo { +template <> struct DenseMapInfo { static wasm::WasmGlobalType getEmptyKey() { return wasm::WasmGlobalType{1, true}; } @@ -64,7 +62,7 @@ template <> struct DenseMapInfo { }; // Traits for using WasmLimits in a DenseMap -template <> struct DenseMapInfo { +template <> struct DenseMapInfo { static wasm::WasmLimits getEmptyKey() { return wasm::WasmLimits{0xff, 0xff, 0xff}; } @@ -86,19 +84,19 @@ template <> struct DenseMapInfo { }; // Traits for using WasmTableType in a DenseMap -template <> struct DenseMapInfo { +template <> struct DenseMapInfo { static wasm::WasmTableType getEmptyKey() { - return wasm::WasmTableType{0, - DenseMapInfo::getEmptyKey()}; + return wasm::WasmTableType{ + 0, DenseMapInfo::getEmptyKey()}; } static wasm::WasmTableType getTombstoneKey() { return wasm::WasmTableType{ - 1, DenseMapInfo::getTombstoneKey()}; + 1, DenseMapInfo::getTombstoneKey()}; } static unsigned getHashValue(const wasm::WasmTableType &TableType) { return hash_combine( TableType.ElemType, - DenseMapInfo::getHashValue(TableType.Limits)); + DenseMapInfo::getHashValue(TableType.Limits)); } static bool isEqual(const wasm::WasmTableType &LHS, const wasm::WasmTableType &RHS) { diff --git a/llvm/include/llvm/BinaryFormat/XCOFF.h b/llvm/include/llvm/BinaryFormat/XCOFF.h index 8a42d26f3f4a..cffd8618f1e3 100644 --- a/llvm/include/llvm/BinaryFormat/XCOFF.h +++ b/llvm/include/llvm/BinaryFormat/XCOFF.h @@ -28,9 +28,14 @@ namespace XCOFF { constexpr size_t FileNamePadSize = 6; constexpr size_t NameSize = 8; constexpr size_t FileHeaderSize32 = 20; +constexpr size_t FileHeaderSize64 = 24; +constexpr size_t AuxFileHeaderSize32 = 72; +constexpr size_t AuxFileHeaderSize64 = 110; constexpr size_t SectionHeaderSize32 = 40; +constexpr size_t SectionHeaderSize64 = 72; constexpr size_t SymbolTableEntrySize = 18; constexpr size_t RelocationSerializationSize32 = 10; +constexpr size_t RelocationSerializationSize64 = 14; constexpr uint16_t RelocOverflow = 65535; constexpr uint8_t AllocRegNo = 31; @@ -38,6 +43,17 @@ enum ReservedSectionNum : int16_t { N_DEBUG = -2, N_ABS = -1, N_UNDEF = 0 }; enum MagicNumber : uint16_t { XCOFF32 = 0x01DF, XCOFF64 = 0x01F7 }; +// This field only exists in the XCOFF64 definition. +enum AuxHeaderFlags64 : uint16_t { + SHR_SYMTAB = 0x8000, ///< At exec time, create shared symbol table for program + ///< (main program only). + FORK_POLICY = 0x4000, ///< Forktree policy specified (main program only). + FORK_COR = 0x2000 ///< If _AOUT_FORK_POLICY is set, specify copy-on-reference + ///< if this bit is set. Specify copy-on- write otherwise. + ///< If _AOUT_FORK_POLICY is 0, this bit is reserved for + ///< future use and should be set to 0. +}; + // x_smclas field of x_csect from system header: /usr/include/syms.h /// Storage Mapping Class definitions. enum StorageMappingClass : uint8_t { diff --git a/llvm/include/llvm/Bitcode/BitcodeAnalyzer.h b/llvm/include/llvm/Bitcode/BitcodeAnalyzer.h index de828be3bf1b..f6fc284da33f 100644 --- a/llvm/include/llvm/Bitcode/BitcodeAnalyzer.h +++ b/llvm/include/llvm/Bitcode/BitcodeAnalyzer.h @@ -42,6 +42,8 @@ struct BCDumpOptions { bool Symbolic = false; /// Print binary blobs using hex escapes. bool ShowBinaryBlobs = false; + /// Print BLOCKINFO block details. + bool DumpBlockinfo = false; BCDumpOptions(raw_ostream &OS) : OS(OS) {} }; diff --git a/llvm/include/llvm/Bitcode/BitcodeCommon.h b/llvm/include/llvm/Bitcode/BitcodeCommon.h index 6a3e74550bc4..22d1872fe49c 100644 --- a/llvm/include/llvm/Bitcode/BitcodeCommon.h +++ b/llvm/include/llvm/Bitcode/BitcodeCommon.h @@ -19,10 +19,14 @@ namespace llvm { struct AllocaPackedValues { - using Align = Bitfield::Element; - using UsedWithInAlloca = Bitfield::Element; + // We increased the number of bits needed to represent alignment to be more + // than 5, but to preserve backward compatibility we store the upper bits + // separately. + using AlignLower = Bitfield::Element; + using UsedWithInAlloca = Bitfield::Element; using ExplicitType = Bitfield::Element; using SwiftError = Bitfield::Element; + using AlignUpper = Bitfield::Element; }; } // namespace llvm diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h index 28870afb2fcb..04eb2739cbd5 100644 --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -671,6 +671,7 @@ enum AttributeKindCodes { ATTR_KIND_SWIFT_ASYNC = 75, ATTR_KIND_NO_SANITIZE_COVERAGE = 76, ATTR_KIND_ELEMENTTYPE = 77, + ATTR_KIND_DISABLE_SANITIZER_INSTRUMENTATION = 78, }; enum ComdatSelectionKindCodes { diff --git a/llvm/include/llvm/CodeGen/Analysis.h b/llvm/include/llvm/CodeGen/Analysis.h index bdfb416d9bd9..60442326d6c7 100644 --- a/llvm/include/llvm/CodeGen/Analysis.h +++ b/llvm/include/llvm/CodeGen/Analysis.h @@ -104,9 +104,12 @@ ISD::CondCode getFCmpCodeWithoutNaN(ISD::CondCode CC); /// getICmpCondCode - Return the ISD condition code corresponding to /// the given LLVM IR integer condition code. -/// ISD::CondCode getICmpCondCode(ICmpInst::Predicate Pred); +/// getICmpCondCode - Return the LLVM IR integer condition code +/// corresponding to the given ISD integer condition code. +ICmpInst::Predicate getICmpCondCode(ISD::CondCode Pred); + /// Test if the given instruction is in a position to be optimized /// with a tail-call. This roughly means that it's in a block with /// a return and there's nothing that needs to be scheduled diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h index 5dea86e67d64..d7d3692877de 100644 --- a/llvm/include/llvm/CodeGen/AsmPrinter.h +++ b/llvm/include/llvm/CodeGen/AsmPrinter.h @@ -41,7 +41,6 @@ class DIEAbbrev; class DwarfDebug; class GCMetadataPrinter; class GCStrategy; -class GlobalIndirectSymbol; class GlobalObject; class GlobalValue; class GlobalVariable; @@ -708,7 +707,7 @@ public: /// ${:comment}. Targets can override this to add support for their own /// strange codes. virtual void PrintSpecial(const MachineInstr *MI, raw_ostream &OS, - const char *Code) const; + StringRef Code) const; /// Print the MachineOperand as a symbol. Targets with complex handling of /// symbol references should override the base implementation. @@ -795,8 +794,8 @@ private: void emitModuleCommandLines(Module &M); GCMetadataPrinter *GetOrCreateGCPrinter(GCStrategy &S); - /// Emit GlobalAlias or GlobalIFunc. - void emitGlobalIndirectSymbol(Module &M, const GlobalIndirectSymbol &GIS); + void emitGlobalAlias(Module &M, const GlobalAlias &GA); + void emitGlobalIFunc(Module &M, const GlobalIFunc &GI); /// This method decides whether the specified basic block requires a label. bool shouldEmitLabelForBasicBlock(const MachineBasicBlock &MBB) const; diff --git a/llvm/include/llvm/CodeGen/BasicTTIImpl.h b/llvm/include/llvm/CodeGen/BasicTTIImpl.h index e3b834ec42c3..324b7dcfb3ac 100644 --- a/llvm/include/llvm/CodeGen/BasicTTIImpl.h +++ b/llvm/include/llvm/CodeGen/BasicTTIImpl.h @@ -22,6 +22,7 @@ #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/OptimizationRemarkEmitter.h" #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/Analysis/TargetTransformInfoImpl.h" #include "llvm/CodeGen/ISDOpcodes.h" @@ -282,6 +283,11 @@ public: return getTLI()->getTargetMachine().getAssumedAddrSpace(V); } + std::pair + getPredicatedAddrSpace(const Value *V) const { + return getTLI()->getTargetMachine().getPredicatedAddrSpace(V); + } + Value *rewriteIntrinsicWithAddressSpace(IntrinsicInst *II, Value *OldV, Value *NewV) const { return nullptr; @@ -363,8 +369,9 @@ public: } InstructionCost getGEPCost(Type *PointeeType, const Value *Ptr, - ArrayRef Operands) { - return BaseT::getGEPCost(PointeeType, Ptr, Operands); + ArrayRef Operands, + TTI::TargetCostKind CostKind) { + return BaseT::getGEPCost(PointeeType, Ptr, Operands, CostKind); } unsigned getEstimatedNumberOfCaseClusters(const SwitchInst &SI, @@ -484,7 +491,8 @@ public: int getInlinerVectorBonusPercent() { return 150; } void getUnrollingPreferences(Loop *L, ScalarEvolution &SE, - TTI::UnrollingPreferences &UP) { + TTI::UnrollingPreferences &UP, + OptimizationRemarkEmitter *ORE) { // This unrolling functionality is target independent, but to provide some // motivation for its intended use, for x86: @@ -526,6 +534,15 @@ public: continue; } + if (ORE) { + ORE->emit([&]() { + return OptimizationRemark("TTI", "DontUnroll", L->getStartLoc(), + L->getHeader()) + << "advising against unrolling the loop because it " + "contains a " + << ore::NV("Call", &I); + }); + } return; } } @@ -653,6 +670,7 @@ public: } Optional getMaxVScale() const { return None; } + Optional getVScaleForTuning() const { return None; } /// Estimate the overhead of scalarizing an instruction. Insert and Extract /// are set if the demanded result elements need to be inserted and/or @@ -686,7 +704,7 @@ public: bool Extract) { auto *Ty = cast(InTy); - APInt DemandedElts = APInt::getAllOnesValue(Ty->getNumElements()); + APInt DemandedElts = APInt::getAllOnes(Ty->getNumElements()); return thisT()->getScalarizationOverhead(Ty, DemandedElts, Insert, Extract); } @@ -737,8 +755,7 @@ public: unsigned getMaxInterleaveFactor(unsigned VF) { return 1; } InstructionCost getArithmeticInstrCost( - unsigned Opcode, Type *Ty, - TTI::TargetCostKind CostKind = TTI::TCK_RecipThroughput, + unsigned Opcode, Type *Ty, TTI::TargetCostKind CostKind, TTI::OperandValueKind Opd1Info = TTI::OK_AnyValue, TTI::OperandValueKind Opd2Info = TTI::OK_AnyValue, TTI::OperandValueProperties Opd1PropInfo = TTI::OP_None, @@ -1102,6 +1119,39 @@ public: return LT.first; } + InstructionCost getReplicationShuffleCost(Type *EltTy, int ReplicationFactor, + int VF, + const APInt &DemandedDstElts, + TTI::TargetCostKind CostKind) { + assert(DemandedDstElts.getBitWidth() == (unsigned)VF * ReplicationFactor && + "Unexpected size of DemandedDstElts."); + + InstructionCost Cost; + + auto *SrcVT = FixedVectorType::get(EltTy, VF); + auto *ReplicatedVT = FixedVectorType::get(EltTy, VF * ReplicationFactor); + + // The Mask shuffling cost is extract all the elements of the Mask + // and insert each of them Factor times into the wide vector: + // + // E.g. an interleaved group with factor 3: + // %mask = icmp ult <8 x i32> %vec1, %vec2 + // %interleaved.mask = shufflevector <8 x i1> %mask, <8 x i1> undef, + // <24 x i32> <0,0,0,1,1,1,2,2,2,3,3,3,4,4,4,5,5,5,6,6,6,7,7,7> + // The cost is estimated as extract all mask elements from the <8xi1> mask + // vector and insert them factor times into the <24xi1> shuffled mask + // vector. + APInt DemandedSrcElts = APIntOps::ScaleBitMask(DemandedDstElts, VF); + Cost += thisT()->getScalarizationOverhead(SrcVT, DemandedSrcElts, + /*Insert*/ false, + /*Extract*/ true); + Cost += + thisT()->getScalarizationOverhead(ReplicatedVT, DemandedDstElts, + /*Insert*/ true, /*Extract*/ false); + + return Cost; + } + InstructionCost getMemoryOpCost(unsigned Opcode, Type *Src, MaybeAlign Alignment, unsigned AddressSpace, TTI::TargetCostKind CostKind, @@ -1201,9 +1251,9 @@ public: // used (those corresponding to elements [0:1] and [8:9] of the unlegalized // type). The other loads are unused. // - // We only scale the cost of loads since interleaved store groups aren't - // allowed to have gaps. - if (Opcode == Instruction::Load && VecTySize > VecTyLTSize) { + // TODO: Note that legalization can turn masked loads/stores into unmasked + // (legalized) loads/stores. This can be reflected in the cost. + if (Cost.isValid() && VecTySize > VecTyLTSize) { // The number of loads of a legal type it will take to represent a load // of the unlegalized vector type. unsigned NumLegalInsts = divideCeil(VecTySize, VecTyLTSize); @@ -1220,10 +1270,24 @@ public: // Scale the cost of the load by the fraction of legal instructions that // will be used. - Cost *= UsedInsts.count() / NumLegalInsts; + Cost = divideCeil(UsedInsts.count() * Cost.getValue().getValue(), + NumLegalInsts); } // Then plus the cost of interleave operation. + assert(Indices.size() <= Factor && + "Interleaved memory op has too many members"); + + const APInt DemandedAllSubElts = APInt::getAllOnes(NumSubElts); + const APInt DemandedAllResultElts = APInt::getAllOnes(NumElts); + + APInt DemandedLoadStoreElts = APInt::getZero(NumElts); + for (unsigned Index : Indices) { + assert(Index < Factor && "Invalid index for interleaved memory op"); + for (unsigned Elm = 0; Elm < NumSubElts; Elm++) + DemandedLoadStoreElts.setBit(Index + Elm * Factor); + } + if (Opcode == Instruction::Load) { // The interleave cost is similar to extract sub vectors' elements // from the wide vector, and insert them into sub vectors. @@ -1233,79 +1297,56 @@ public: // %v0 = shuffle %vec, undef, <0, 2, 4, 6> ; Index 0 // The cost is estimated as extract elements at 0, 2, 4, 6 from the // <8 x i32> vector and insert them into a <4 x i32> vector. - - assert(Indices.size() <= Factor && - "Interleaved memory op has too many members"); - - for (unsigned Index : Indices) { - assert(Index < Factor && "Invalid index for interleaved memory op"); - - // Extract elements from loaded vector for each sub vector. - for (unsigned i = 0; i < NumSubElts; i++) - Cost += thisT()->getVectorInstrCost(Instruction::ExtractElement, VT, - Index + i * Factor); - } - - InstructionCost InsSubCost = 0; - for (unsigned i = 0; i < NumSubElts; i++) - InsSubCost += - thisT()->getVectorInstrCost(Instruction::InsertElement, SubVT, i); - + InstructionCost InsSubCost = + thisT()->getScalarizationOverhead(SubVT, DemandedAllSubElts, + /*Insert*/ true, /*Extract*/ false); Cost += Indices.size() * InsSubCost; + Cost += + thisT()->getScalarizationOverhead(VT, DemandedLoadStoreElts, + /*Insert*/ false, /*Extract*/ true); } else { - // The interleave cost is extract all elements from sub vectors, and + // The interleave cost is extract elements from sub vectors, and // insert them into the wide vector. // - // E.g. An interleaved store of factor 2: - // %v0_v1 = shuffle %v0, %v1, <0, 4, 1, 5, 2, 6, 3, 7> - // store <8 x i32> %interleaved.vec, <8 x i32>* %ptr - // The cost is estimated as extract all elements from both <4 x i32> - // vectors and insert into the <8 x i32> vector. - - InstructionCost ExtSubCost = 0; - for (unsigned i = 0; i < NumSubElts; i++) - ExtSubCost += - thisT()->getVectorInstrCost(Instruction::ExtractElement, SubVT, i); - Cost += ExtSubCost * Factor; - - for (unsigned i = 0; i < NumElts; i++) - Cost += static_cast(this) - ->getVectorInstrCost(Instruction::InsertElement, VT, i); + // E.g. An interleaved store of factor 3 with 2 members at indices 0,1: + // (using VF=4): + // %v0_v1 = shuffle %v0, %v1, <0,4,undef,1,5,undef,2,6,undef,3,7,undef> + // %gaps.mask = + // call llvm.masked.store <12 x i32> %v0_v1, <12 x i32>* %ptr, + // i32 Align, <12 x i1> %gaps.mask + // The cost is estimated as extract all elements (of actual members, + // excluding gaps) from both <4 x i32> vectors and insert into the <12 x + // i32> vector. + InstructionCost ExtSubCost = + thisT()->getScalarizationOverhead(SubVT, DemandedAllSubElts, + /*Insert*/ false, /*Extract*/ true); + Cost += ExtSubCost * Indices.size(); + Cost += thisT()->getScalarizationOverhead(VT, DemandedLoadStoreElts, + /*Insert*/ true, + /*Extract*/ false); } if (!UseMaskForCond) return Cost; Type *I8Type = Type::getInt8Ty(VT->getContext()); - auto *MaskVT = FixedVectorType::get(I8Type, NumElts); - SubVT = FixedVectorType::get(I8Type, NumSubElts); - - // The Mask shuffling cost is extract all the elements of the Mask - // and insert each of them Factor times into the wide vector: - // - // E.g. an interleaved group with factor 3: - // %mask = icmp ult <8 x i32> %vec1, %vec2 - // %interleaved.mask = shufflevector <8 x i1> %mask, <8 x i1> undef, - // <24 x i32> <0,0,0,1,1,1,2,2,2,3,3,3,4,4,4,5,5,5,6,6,6,7,7,7> - // The cost is estimated as extract all mask elements from the <8xi1> mask - // vector and insert them factor times into the <24xi1> shuffled mask - // vector. - for (unsigned i = 0; i < NumSubElts; i++) - Cost += - thisT()->getVectorInstrCost(Instruction::ExtractElement, SubVT, i); - for (unsigned i = 0; i < NumElts; i++) - Cost += - thisT()->getVectorInstrCost(Instruction::InsertElement, MaskVT, i); + Cost += thisT()->getReplicationShuffleCost( + I8Type, Factor, NumSubElts, + UseMaskForGaps ? DemandedLoadStoreElts : DemandedAllResultElts, + CostKind); // The Gaps mask is invariant and created outside the loop, therefore the // cost of creating it is not accounted for here. However if we have both // a MaskForGaps and some other mask that guards the execution of the // memory access, we need to account for the cost of And-ing the two masks // inside the loop. - if (UseMaskForGaps) + if (UseMaskForGaps) { + auto *MaskVT = FixedVectorType::get(I8Type, NumElts); Cost += thisT()->getArithmeticInstrCost(BinaryOperator::And, MaskVT, CostKind); + } return Cost; } @@ -1460,10 +1501,10 @@ public: Type *CondTy = RetTy->getWithNewBitWidth(1); Cost += thisT()->getCmpSelInstrCost(BinaryOperator::ICmp, RetTy, CondTy, - CmpInst::BAD_ICMP_PREDICATE, CostKind); + CmpInst::ICMP_EQ, CostKind); Cost += thisT()->getCmpSelInstrCost(BinaryOperator::Select, RetTy, CondTy, - CmpInst::BAD_ICMP_PREDICATE, CostKind); + CmpInst::ICMP_EQ, CostKind); } return Cost; } @@ -1689,26 +1730,34 @@ public: return thisT()->getMinMaxReductionCost( VecOpTy, cast(CmpInst::makeCmpResultType(VecOpTy)), /*IsUnsigned=*/true, CostKind); - case Intrinsic::abs: + case Intrinsic::abs: { + // abs(X) = select(icmp(X,0),X,sub(0,X)) + Type *CondTy = RetTy->getWithNewBitWidth(1); + CmpInst::Predicate Pred = CmpInst::ICMP_SGT; + InstructionCost Cost = 0; + Cost += thisT()->getCmpSelInstrCost(BinaryOperator::ICmp, RetTy, CondTy, + Pred, CostKind); + Cost += thisT()->getCmpSelInstrCost(BinaryOperator::Select, RetTy, CondTy, + Pred, CostKind); + // TODO: Should we add an OperandValueProperties::OP_Zero property? + Cost += thisT()->getArithmeticInstrCost( + BinaryOperator::Sub, RetTy, CostKind, TTI::OK_UniformConstantValue); + return Cost; + } case Intrinsic::smax: case Intrinsic::smin: case Intrinsic::umax: case Intrinsic::umin: { - // abs(X) = select(icmp(X,0),X,sub(0,X)) // minmax(X,Y) = select(icmp(X,Y),X,Y) Type *CondTy = RetTy->getWithNewBitWidth(1); + bool IsUnsigned = IID == Intrinsic::umax || IID == Intrinsic::umin; + CmpInst::Predicate Pred = + IsUnsigned ? CmpInst::ICMP_UGT : CmpInst::ICMP_SGT; InstructionCost Cost = 0; - // TODO: Ideally getCmpSelInstrCost would accept an icmp condition code. - Cost += - thisT()->getCmpSelInstrCost(BinaryOperator::ICmp, RetTy, CondTy, - CmpInst::BAD_ICMP_PREDICATE, CostKind); - Cost += - thisT()->getCmpSelInstrCost(BinaryOperator::Select, RetTy, CondTy, - CmpInst::BAD_ICMP_PREDICATE, CostKind); - // TODO: Should we add an OperandValueProperties::OP_Zero property? - if (IID == Intrinsic::abs) - Cost += thisT()->getArithmeticInstrCost( - BinaryOperator::Sub, RetTy, CostKind, TTI::OK_UniformConstantValue); + Cost += thisT()->getCmpSelInstrCost(BinaryOperator::ICmp, RetTy, CondTy, + Pred, CostKind); + Cost += thisT()->getCmpSelInstrCost(BinaryOperator::Select, RetTy, CondTy, + Pred, CostKind); return Cost; } case Intrinsic::sadd_sat: @@ -1719,6 +1768,7 @@ public: Intrinsic::ID OverflowOp = IID == Intrinsic::sadd_sat ? Intrinsic::sadd_with_overflow : Intrinsic::ssub_with_overflow; + CmpInst::Predicate Pred = CmpInst::ICMP_SGT; // SatMax -> Overflow && SumDiff < 0 // SatMin -> Overflow && SumDiff >= 0 @@ -1726,12 +1776,10 @@ public: IntrinsicCostAttributes Attrs(OverflowOp, OpTy, {RetTy, RetTy}, FMF, nullptr, ScalarizationCostPassed); Cost += thisT()->getIntrinsicInstrCost(Attrs, CostKind); - Cost += - thisT()->getCmpSelInstrCost(BinaryOperator::ICmp, RetTy, CondTy, - CmpInst::BAD_ICMP_PREDICATE, CostKind); - Cost += 2 * thisT()->getCmpSelInstrCost( - BinaryOperator::Select, RetTy, CondTy, - CmpInst::BAD_ICMP_PREDICATE, CostKind); + Cost += thisT()->getCmpSelInstrCost(BinaryOperator::ICmp, RetTy, CondTy, + Pred, CostKind); + Cost += 2 * thisT()->getCmpSelInstrCost(BinaryOperator::Select, RetTy, + CondTy, Pred, CostKind); return Cost; } case Intrinsic::uadd_sat: @@ -1784,23 +1832,16 @@ public: ? BinaryOperator::Add : BinaryOperator::Sub; - // LHSSign -> LHS >= 0 - // RHSSign -> RHS >= 0 - // SumSign -> Sum >= 0 - // // Add: - // Overflow -> (LHSSign == RHSSign) && (LHSSign != SumSign) + // Overflow -> (Result < LHS) ^ (RHS < 0) // Sub: - // Overflow -> (LHSSign != RHSSign) && (LHSSign != SumSign) + // Overflow -> (Result < LHS) ^ (RHS > 0) InstructionCost Cost = 0; Cost += thisT()->getArithmeticInstrCost(Opcode, SumTy, CostKind); - Cost += 3 * thisT()->getCmpSelInstrCost( - Instruction::ICmp, SumTy, OverflowTy, - CmpInst::BAD_ICMP_PREDICATE, CostKind); Cost += 2 * thisT()->getCmpSelInstrCost( - Instruction::Select, OverflowTy, OverflowTy, - CmpInst::BAD_ICMP_PREDICATE, CostKind); - Cost += thisT()->getArithmeticInstrCost(BinaryOperator::And, OverflowTy, + Instruction::ICmp, SumTy, OverflowTy, + CmpInst::ICMP_SGT, CostKind); + Cost += thisT()->getArithmeticInstrCost(BinaryOperator::Xor, OverflowTy, CostKind); return Cost; } @@ -1811,12 +1852,15 @@ public: unsigned Opcode = IID == Intrinsic::uadd_with_overflow ? BinaryOperator::Add : BinaryOperator::Sub; + CmpInst::Predicate Pred = IID == Intrinsic::uadd_with_overflow + ? CmpInst::ICMP_ULT + : CmpInst::ICMP_UGT; InstructionCost Cost = 0; Cost += thisT()->getArithmeticInstrCost(Opcode, SumTy, CostKind); Cost += thisT()->getCmpSelInstrCost(BinaryOperator::ICmp, SumTy, OverflowTy, - CmpInst::BAD_ICMP_PREDICATE, CostKind); + Pred, CostKind); return Cost; } case Intrinsic::smul_with_overflow: @@ -1825,9 +1869,9 @@ public: Type *OverflowTy = RetTy->getContainedType(1); unsigned ExtSize = MulTy->getScalarSizeInBits() * 2; Type *ExtTy = MulTy->getWithNewBitWidth(ExtSize); + bool IsSigned = IID == Intrinsic::smul_with_overflow; - unsigned ExtOp = - IID == Intrinsic::smul_fix ? Instruction::SExt : Instruction::ZExt; + unsigned ExtOp = IsSigned ? Instruction::SExt : Instruction::ZExt; TTI::CastContextHint CCH = TTI::CastContextHint::None; InstructionCost Cost = 0; @@ -1836,18 +1880,17 @@ public: thisT()->getArithmeticInstrCost(Instruction::Mul, ExtTy, CostKind); Cost += 2 * thisT()->getCastInstrCost(Instruction::Trunc, MulTy, ExtTy, CCH, CostKind); - Cost += thisT()->getArithmeticInstrCost(Instruction::LShr, MulTy, + Cost += thisT()->getArithmeticInstrCost(Instruction::LShr, ExtTy, CostKind, TTI::OK_AnyValue, TTI::OK_UniformConstantValue); - if (IID == Intrinsic::smul_with_overflow) + if (IsSigned) Cost += thisT()->getArithmeticInstrCost(Instruction::AShr, MulTy, CostKind, TTI::OK_AnyValue, TTI::OK_UniformConstantValue); - Cost += - thisT()->getCmpSelInstrCost(BinaryOperator::ICmp, MulTy, OverflowTy, - CmpInst::BAD_ICMP_PREDICATE, CostKind); + Cost += thisT()->getCmpSelInstrCost( + BinaryOperator::ICmp, MulTy, OverflowTy, CmpInst::ICMP_NE, CostKind); return Cost; } case Intrinsic::ctpop: @@ -1974,16 +2017,16 @@ public: /// \param RetTy Return value types. /// \param Tys Argument types. /// \returns The cost of Call instruction. - InstructionCost - getCallInstrCost(Function *F, Type *RetTy, ArrayRef Tys, - TTI::TargetCostKind CostKind = TTI::TCK_SizeAndLatency) { + InstructionCost getCallInstrCost(Function *F, Type *RetTy, + ArrayRef Tys, + TTI::TargetCostKind CostKind) { return 10; } unsigned getNumberOfParts(Type *Tp) { std::pair LT = getTLI()->getTypeLegalizationCost(DL, Tp); - return *LT.first.getValue(); + return LT.first.isValid() ? *LT.first.getValue() : 0; } InstructionCost getAddressComputationCost(Type *Ty, ScalarEvolution *, @@ -2060,7 +2103,8 @@ public: // By default reductions need one shuffle per reduction level. ShuffleCost += NumReduxLevels * thisT()->getShuffleCost( TTI::SK_PermuteSingleSrc, Ty, None, 0, Ty); - ArithCost += NumReduxLevels * thisT()->getArithmeticInstrCost(Opcode, Ty); + ArithCost += + NumReduxLevels * thisT()->getArithmeticInstrCost(Opcode, Ty, CostKind); return ShuffleCost + ArithCost + thisT()->getVectorInstrCost(Instruction::ExtractElement, Ty, 0); } diff --git a/llvm/include/llvm/CodeGen/CodeGenCommonISel.h b/llvm/include/llvm/CodeGen/CodeGenCommonISel.h new file mode 100644 index 000000000000..270f935b6738 --- /dev/null +++ b/llvm/include/llvm/CodeGen/CodeGenCommonISel.h @@ -0,0 +1,219 @@ +//===- CodeGenCommonISel.h - Common code between ISels ---------*- C++ -*--===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares common utilities that are shared between SelectionDAG and +// GlobalISel frameworks. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CODEGEN_CODEGENCOMMONISEL_H +#define LLVM_CODEGEN_CODEGENCOMMONISEL_H + +#include "llvm/CodeGen/MachineBasicBlock.h" +#include +namespace llvm { + +class BasicBlock; +class MachineBasicBlock; +/// Encapsulates all of the information needed to generate a stack protector +/// check, and signals to isel when initialized that one needs to be generated. +/// +/// *NOTE* The following is a high level documentation of SelectionDAG Stack +/// Protector Generation. This is now also ported be shared with GlobalISel, +/// but without any significant changes. +/// +/// High Level Overview of ISel Stack Protector Generation: +/// +/// Previously, the "stack protector" IR pass handled stack protector +/// generation. This necessitated splitting basic blocks at the IR level to +/// create the success/failure basic blocks in the tail of the basic block in +/// question. As a result of this, calls that would have qualified for the +/// sibling call optimization were no longer eligible for optimization since +/// said calls were no longer right in the "tail position" (i.e. the immediate +/// predecessor of a ReturnInst instruction). +/// +/// Since the sibling call optimization causes the callee to reuse the caller's +/// stack, if we could delay the generation of the stack protector check until +/// later in CodeGen after the sibling call decision was made, we get both the +/// tail call optimization and the stack protector check! +/// +/// A few goals in solving this problem were: +/// +/// 1. Preserve the architecture independence of stack protector generation. +/// +/// 2. Preserve the normal IR level stack protector check for platforms like +/// OpenBSD for which we support platform-specific stack protector +/// generation. +/// +/// The main problem that guided the present solution is that one can not +/// solve this problem in an architecture independent manner at the IR level +/// only. This is because: +/// +/// 1. The decision on whether or not to perform a sibling call on certain +/// platforms (for instance i386) requires lower level information +/// related to available registers that can not be known at the IR level. +/// +/// 2. Even if the previous point were not true, the decision on whether to +/// perform a tail call is done in LowerCallTo in SelectionDAG (or +/// CallLowering in GlobalISel) which occurs after the Stack Protector +/// Pass. As a result, one would need to put the relevant callinst into the +/// stack protector check success basic block (where the return inst is +/// placed) and then move it back later at ISel/MI time before the +/// stack protector check if the tail call optimization failed. The MI +/// level option was nixed immediately since it would require +/// platform-specific pattern matching. The ISel level option was +/// nixed because SelectionDAG only processes one IR level basic block at a +/// time implying one could not create a DAG Combine to move the callinst. +/// +/// To get around this problem: +/// +/// 1. SelectionDAG can only process one block at a time, we can generate +/// multiple machine basic blocks for one IR level basic block. +/// This is how we handle bit tests and switches. +/// +/// 2. At the MI level, tail calls are represented via a special return +/// MIInst called "tcreturn". Thus if we know the basic block in which we +/// wish to insert the stack protector check, we get the correct behavior +/// by always inserting the stack protector check right before the return +/// statement. This is a "magical transformation" since no matter where +/// the stack protector check intrinsic is, we always insert the stack +/// protector check code at the end of the BB. +/// +/// Given the aforementioned constraints, the following solution was devised: +/// +/// 1. On platforms that do not support ISel stack protector check +/// generation, allow for the normal IR level stack protector check +/// generation to continue. +/// +/// 2. On platforms that do support ISel stack protector check +/// generation: +/// +/// a. Use the IR level stack protector pass to decide if a stack +/// protector is required/which BB we insert the stack protector check +/// in by reusing the logic already therein. +/// +/// b. After we finish selecting the basic block, we produce the validation +/// code with one of these techniques: +/// 1) with a call to a guard check function +/// 2) with inlined instrumentation +/// +/// 1) We insert a call to the check function before the terminator. +/// +/// 2) We first find a splice point in the parent basic block +/// before the terminator and then splice the terminator of said basic +/// block into the success basic block. Then we code-gen a new tail for +/// the parent basic block consisting of the two loads, the comparison, +/// and finally two branches to the success/failure basic blocks. We +/// conclude by code-gening the failure basic block if we have not +/// code-gened it already (all stack protector checks we generate in +/// the same function, use the same failure basic block). +class StackProtectorDescriptor { +public: + StackProtectorDescriptor() = default; + + /// Returns true if all fields of the stack protector descriptor are + /// initialized implying that we should/are ready to emit a stack protector. + bool shouldEmitStackProtector() const { + return ParentMBB && SuccessMBB && FailureMBB; + } + + bool shouldEmitFunctionBasedCheckStackProtector() const { + return ParentMBB && !SuccessMBB && !FailureMBB; + } + + /// Initialize the stack protector descriptor structure for a new basic + /// block. + void initialize(const BasicBlock *BB, MachineBasicBlock *MBB, + bool FunctionBasedInstrumentation) { + // Make sure we are not initialized yet. + assert(!shouldEmitStackProtector() && "Stack Protector Descriptor is " + "already initialized!"); + ParentMBB = MBB; + if (!FunctionBasedInstrumentation) { + SuccessMBB = addSuccessorMBB(BB, MBB, /* IsLikely */ true); + FailureMBB = addSuccessorMBB(BB, MBB, /* IsLikely */ false, FailureMBB); + } + } + + /// Reset state that changes when we handle different basic blocks. + /// + /// This currently includes: + /// + /// 1. The specific basic block we are generating a + /// stack protector for (ParentMBB). + /// + /// 2. The successor machine basic block that will contain the tail of + /// parent mbb after we create the stack protector check (SuccessMBB). This + /// BB is visited only on stack protector check success. + void resetPerBBState() { + ParentMBB = nullptr; + SuccessMBB = nullptr; + } + + /// Reset state that only changes when we switch functions. + /// + /// This currently includes: + /// + /// 1. FailureMBB since we reuse the failure code path for all stack + /// protector checks created in an individual function. + /// + /// 2.The guard variable since the guard variable we are checking against is + /// always the same. + void resetPerFunctionState() { FailureMBB = nullptr; } + + MachineBasicBlock *getParentMBB() { return ParentMBB; } + MachineBasicBlock *getSuccessMBB() { return SuccessMBB; } + MachineBasicBlock *getFailureMBB() { return FailureMBB; } + +private: + /// The basic block for which we are generating the stack protector. + /// + /// As a result of stack protector generation, we will splice the + /// terminators of this basic block into the successor mbb SuccessMBB and + /// replace it with a compare/branch to the successor mbbs + /// SuccessMBB/FailureMBB depending on whether or not the stack protector + /// was violated. + MachineBasicBlock *ParentMBB = nullptr; + + /// A basic block visited on stack protector check success that contains the + /// terminators of ParentMBB. + MachineBasicBlock *SuccessMBB = nullptr; + + /// This basic block visited on stack protector check failure that will + /// contain a call to __stack_chk_fail(). + MachineBasicBlock *FailureMBB = nullptr; + + /// Add a successor machine basic block to ParentMBB. If the successor mbb + /// has not been created yet (i.e. if SuccMBB = 0), then the machine basic + /// block will be created. Assign a large weight if IsLikely is true. + MachineBasicBlock *addSuccessorMBB(const BasicBlock *BB, + MachineBasicBlock *ParentMBB, + bool IsLikely, + MachineBasicBlock *SuccMBB = nullptr); +}; + +/// Find the split point at which to splice the end of BB into its success stack +/// protector check machine basic block. +/// +/// On many platforms, due to ABI constraints, terminators, even before register +/// allocation, use physical registers. This creates an issue for us since +/// physical registers at this point can not travel across basic +/// blocks. Luckily, selectiondag always moves physical registers into vregs +/// when they enter functions and moves them through a sequence of copies back +/// into the physical registers right before the terminator creating a +/// ``Terminator Sequence''. This function is searching for the beginning of the +/// terminator sequence so that we can ensure that we splice off not just the +/// terminator, but additionally the copies that move the vregs into the +/// physical registers. +MachineBasicBlock::iterator +findSplitPointForStackProtector(MachineBasicBlock *BB, + const TargetInstrInfo &TII); + +} // namespace llvm + +#endif // LLVM_CODEGEN_CODEGENCOMMONISEL_H diff --git a/llvm/include/llvm/CodeGen/CommandFlags.h b/llvm/include/llvm/CodeGen/CommandFlags.h index 5a4351756297..ed3cd54df272 100644 --- a/llvm/include/llvm/CodeGen/CommandFlags.h +++ b/llvm/include/llvm/CodeGen/CommandFlags.h @@ -48,7 +48,6 @@ Optional getExplicitCodeModel(); llvm::ExceptionHandling getExceptionModel(); -CodeGenFileType getFileType(); Optional getExplicitFileType(); CodeGenFileType getFileType(); @@ -74,6 +73,8 @@ llvm::FloatABI::ABIType getFloatABIForCalls(); llvm::FPOpFusion::FPOpFusionMode getFuseFPOps(); +SwiftAsyncFramePointerMode getSwiftAsyncFramePointer(); + bool getDontPlaceZerosInBSS(); bool getEnableGuaranteedTailCallOpt(); @@ -128,8 +129,6 @@ bool getEnableMachineFunctionSplitter(); bool getEnableDebugEntryValues(); -bool getPseudoProbeForProfiling(); - bool getValueTrackingVariableLocations(); bool getForceDwarfFrameSection(); @@ -138,6 +137,8 @@ bool getXRayOmitFunctionIndex(); bool getDebugStrictDwarf(); +unsigned getAlignLoops(); + /// Create this object with static storage to register codegen-related command /// line options. struct RegisterCodeGenFlags { diff --git a/llvm/include/llvm/CodeGen/FunctionLoweringInfo.h b/llvm/include/llvm/CodeGen/FunctionLoweringInfo.h index b6bde0249f88..524730d53694 100644 --- a/llvm/include/llvm/CodeGen/FunctionLoweringInfo.h +++ b/llvm/include/llvm/CodeGen/FunctionLoweringInfo.h @@ -17,7 +17,6 @@ #include "llvm/ADT/BitVector.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/IndexedMap.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/CodeGen/ISDOpcodes.h" diff --git a/llvm/include/llvm/CodeGen/GlobalISel/CallLowering.h b/llvm/include/llvm/CodeGen/GlobalISel/CallLowering.h index 6bdaddd9c6f5..9c878d4b087b 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/CallLowering.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/CallLowering.h @@ -116,6 +116,9 @@ public: /// vreg that the swifterror should be copied into after the call. Register SwiftErrorVReg; + /// Original IR callsite corresponding to this call, if available. + const CallBase *CB = nullptr; + MDNode *KnownCallees = nullptr; /// True if the call must be tail call optimized. @@ -259,7 +262,7 @@ public: /// handle the appropriate COPY (either to or from) and mark any /// relevant uses/defines as needed. virtual void assignValueToReg(Register ValVReg, Register PhysReg, - CCValAssign &VA) = 0; + CCValAssign VA) = 0; /// The specified value has been assigned to a stack /// location. Load or store it there, with appropriate extension @@ -279,11 +282,14 @@ public: } /// Handle custom values, which may be passed into one or more of \p VAs. + /// \p If the handler wants the assignments to be delayed until after + /// mem loc assignments, then it sets \p Thunk to the thunk to do the + /// assignment. /// \return The number of \p VAs that have been assigned after the first /// one, and which should therefore be skipped from further /// processing. - virtual unsigned assignCustomValue(ArgInfo &Arg, - ArrayRef VAs) { + virtual unsigned assignCustomValue(ArgInfo &Arg, ArrayRef VAs, + std::function *Thunk = nullptr) { // This is not a pure virtual method because not all targets need to worry // about custom values. llvm_unreachable("Custom values not supported"); @@ -315,7 +321,7 @@ public: /// Provides a default implementation for argument handling. void assignValueToReg(Register ValVReg, Register PhysReg, - CCValAssign &VA) override; + CCValAssign VA) override; }; /// Base class for ValueHandlers used for arguments passed to a function call, diff --git a/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h b/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h index 56459b68dce0..ff4ad4b72636 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h @@ -36,7 +36,10 @@ class GISelKnownBits; class MachineDominatorTree; class LegalizerInfo; struct LegalityQuery; +class RegisterBank; +class RegisterBankInfo; class TargetLowering; +class TargetRegisterInfo; struct PreferredTuple { LLT Ty; // The result type of the extend. @@ -54,6 +57,7 @@ struct IndexedLoadStoreMatchInfo { struct PtrAddChain { int64_t Imm; Register Base; + const RegisterBank *Bank; }; struct RegisterImmPair { @@ -68,6 +72,16 @@ struct ShiftOfShiftedLogic { uint64_t ValSum; }; +using BuildFnTy = std::function; + +struct MergeTruncStoresInfo { + SmallVector FoundStores; + GStore *LowestIdxStore = nullptr; + Register WideSrcVal; + bool NeedBSwap = false; + bool NeedRotate = false; +}; + using OperandBuildSteps = SmallVector, 4>; struct InstructionBuildSteps { @@ -95,6 +109,8 @@ protected: GISelKnownBits *KB; MachineDominatorTree *MDT; const LegalizerInfo *LI; + const RegisterBankInfo *RBI; + const TargetRegisterInfo *TRI; public: CombinerHelper(GISelChangeObserver &Observer, MachineIRBuilder &B, @@ -120,6 +136,22 @@ public: void replaceRegOpWith(MachineRegisterInfo &MRI, MachineOperand &FromRegOp, Register ToReg) const; + /// Replace the opcode in instruction with a new opcode and inform the + /// observer of the changes. + void replaceOpcodeWith(MachineInstr &FromMI, unsigned ToOpcode) const; + + /// Get the register bank of \p Reg. + /// If Reg has not been assigned a register, a register class, + /// or a register bank, then this returns nullptr. + /// + /// \pre Reg.isValid() + const RegisterBank *getRegBank(Register Reg) const; + + /// Set the register bank of \p Reg. + /// Does nothing if the RegBank is null. + /// This is the counterpart to getRegBank. + void setRegBank(Register Reg, const RegisterBank *RegBank); + /// If \p MI is COPY, try to combine it. /// Returns true if MI changed. bool tryCombineCopy(MachineInstr &MI); @@ -144,6 +176,9 @@ public: bool matchCombineExtendingLoads(MachineInstr &MI, PreferredTuple &MatchInfo); void applyCombineExtendingLoads(MachineInstr &MI, PreferredTuple &MatchInfo); + /// Match (and (load x), mask) -> zextload x + bool matchCombineLoadWithAndMask(MachineInstr &MI, BuildFnTy &MatchInfo); + /// Combine \p MI into a pre-indexed or post-indexed load/store operation if /// legal and the surrounding code makes it useful. bool tryCombineIndexedLoadStore(MachineInstr &MI); @@ -341,6 +376,9 @@ public: bool matchCombineFAbsOfFAbs(MachineInstr &MI, Register &Src); void applyCombineFAbsOfFAbs(MachineInstr &MI, Register &Src); + /// Transform fabs(fneg(x)) to fabs(x). + bool matchCombineFAbsOfFNeg(MachineInstr &MI, BuildFnTy &MatchInfo); + /// Transform trunc ([asz]ext x) to x or ([asz]ext x) or (trunc x). bool matchCombineTruncOfExt(MachineInstr &MI, std::pair &MatchInfo); @@ -445,7 +483,7 @@ public: /// Fold and(and(x, C1), C2) -> C1&C2 ? and(x, C1&C2) : 0 bool matchOverlappingAnd(MachineInstr &MI, - std::function &MatchInfo); + BuildFnTy &MatchInfo); /// \return true if \p MI is a G_AND instruction whose operands are x and y /// where x & y == x or x & y == y. (E.g., one of operands is all-ones value.) @@ -501,8 +539,10 @@ public: /// /// And check if the tree can be replaced with a M-bit load + possibly a /// bswap. - bool matchLoadOrCombine(MachineInstr &MI, - std::function &MatchInfo); + bool matchLoadOrCombine(MachineInstr &MI, BuildFnTy &MatchInfo); + + bool matchTruncStoreMerge(MachineInstr &MI, MergeTruncStoresInfo &MatchInfo); + void applyTruncStoreMerge(MachineInstr &MI, MergeTruncStoresInfo &MatchInfo); bool matchExtendThroughPhis(MachineInstr &MI, MachineInstr *&ExtMI); void applyExtendThroughPhis(MachineInstr &MI, MachineInstr *&ExtMI); @@ -519,12 +559,10 @@ public: /// Use a function which takes in a MachineIRBuilder to perform a combine. /// By default, it erases the instruction \p MI from the function. - void applyBuildFn(MachineInstr &MI, - std::function &MatchInfo); + void applyBuildFn(MachineInstr &MI, BuildFnTy &MatchInfo); /// Use a function which takes in a MachineIRBuilder to perform a combine. /// This variant does not erase \p MI after calling the build function. - void applyBuildFnNoErase(MachineInstr &MI, - std::function &MatchInfo); + void applyBuildFnNoErase(MachineInstr &MI, BuildFnTy &MatchInfo); bool matchFunnelShiftToRotate(MachineInstr &MI); void applyFunnelShiftToRotate(MachineInstr &MI); @@ -535,21 +573,57 @@ public: /// or false constant based off of KnownBits information. bool matchICmpToTrueFalseKnownBits(MachineInstr &MI, int64_t &MatchInfo); - bool matchBitfieldExtractFromSExtInReg( - MachineInstr &MI, std::function &MatchInfo); - /// Match: and (lshr x, cst), mask -> ubfx x, cst, width - bool matchBitfieldExtractFromAnd( - MachineInstr &MI, std::function &MatchInfo); + /// \returns true if a G_ICMP \p MI can be replaced with its LHS based off of + /// KnownBits information. + bool + matchICmpToLHSKnownBits(MachineInstr &MI, + BuildFnTy &MatchInfo); + + /// \returns true if (and (or x, c1), c2) can be replaced with (and x, c2) + bool matchAndOrDisjointMask(MachineInstr &MI, BuildFnTy &MatchInfo); + bool matchBitfieldExtractFromSExtInReg(MachineInstr &MI, + BuildFnTy &MatchInfo); + /// Match: and (lshr x, cst), mask -> ubfx x, cst, width + bool matchBitfieldExtractFromAnd(MachineInstr &MI, BuildFnTy &MatchInfo); + + /// Match: shr (shl x, n), k -> sbfx/ubfx x, pos, width + bool matchBitfieldExtractFromShr(MachineInstr &MI, BuildFnTy &MatchInfo); + + /// Match: shr (and x, n), k -> ubfx x, pos, width + bool matchBitfieldExtractFromShrAnd(MachineInstr &MI, BuildFnTy &MatchInfo); + + // Helpers for reassociation: + bool matchReassocConstantInnerRHS(GPtrAdd &MI, MachineInstr *RHS, + BuildFnTy &MatchInfo); + bool matchReassocFoldConstantsInSubTree(GPtrAdd &MI, MachineInstr *LHS, + MachineInstr *RHS, + BuildFnTy &MatchInfo); + bool matchReassocConstantInnerLHS(GPtrAdd &MI, MachineInstr *LHS, + MachineInstr *RHS, BuildFnTy &MatchInfo); /// Reassociate pointer calculations with G_ADD involved, to allow better /// addressing mode usage. - bool matchReassocPtrAdd(MachineInstr &MI, - std::function &MatchInfo); - + bool matchReassocPtrAdd(MachineInstr &MI, BuildFnTy &MatchInfo); /// Do constant folding when opportunities are exposed after MIR building. bool matchConstantFold(MachineInstr &MI, APInt &MatchInfo); + /// \returns true if it is possible to narrow the width of a scalar binop + /// feeding a G_AND instruction \p MI. + bool matchNarrowBinopFeedingAnd(MachineInstr &MI, BuildFnTy &MatchInfo); + + /// Given an G_UDIV \p MI expressing a divide by constant, return an + /// expression that implements it by multiplying by a magic number. + /// Ref: "Hacker's Delight" or "The PowerPC Compiler Writer's Guide". + MachineInstr *buildUDivUsingMul(MachineInstr &MI); + /// Combine G_UDIV by constant into a multiply by magic constant. + bool matchUDivByConst(MachineInstr &MI); + void applyUDivByConst(MachineInstr &MI); + + // G_UMULH x, (1 << c)) -> x >> (bitwidth - c) + bool matchUMulHToLShr(MachineInstr &MI); + void applyUMulHToLShr(MachineInstr &MI); + /// Try to transform \p MI by using all of the above /// combine functions. Returns true if changed. bool tryCombine(MachineInstr &MI); @@ -560,20 +634,21 @@ public: /// and rename: s/bool tryEmit/void emit/ bool tryEmitMemcpyInline(MachineInstr &MI); -private: - // Memcpy family optimization helpers. - bool tryEmitMemcpyInline(MachineInstr &MI, Register Dst, Register Src, - uint64_t KnownLen, Align DstAlign, Align SrcAlign, - bool IsVolatile); - bool optimizeMemcpy(MachineInstr &MI, Register Dst, Register Src, - uint64_t KnownLen, uint64_t Limit, Align DstAlign, - Align SrcAlign, bool IsVolatile); - bool optimizeMemmove(MachineInstr &MI, Register Dst, Register Src, - uint64_t KnownLen, Align DstAlign, Align SrcAlign, - bool IsVolatile); - bool optimizeMemset(MachineInstr &MI, Register Dst, Register Val, - uint64_t KnownLen, Align DstAlign, bool IsVolatile); + /// Match: + /// (G_UMULO x, 2) -> (G_UADDO x, x) + /// (G_SMULO x, 2) -> (G_SADDO x, x) + bool matchMulOBy2(MachineInstr &MI, BuildFnTy &MatchInfo); + /// Transform (fadd x, fneg(y)) -> (fsub x, y) + /// (fadd fneg(x), y) -> (fsub y, x) + /// (fsub x, fneg(y)) -> (fadd x, y) + /// (fmul fneg(x), fneg(y)) -> (fmul x, y) + /// (fdiv fneg(x), fneg(y)) -> (fdiv x, y) + /// (fmad fneg(x), fneg(y), z) -> (fmad x, y, z) + /// (fma fneg(x), fneg(y), z) -> (fma x, y, z) + bool matchRedundantNegOperands(MachineInstr &MI, BuildFnTy &MatchInfo); + +private: /// Given a non-indexed load or store instruction \p MI, find an offset that /// can be usefully and legally folded into it as a post-indexing operation. /// diff --git a/llvm/include/llvm/CodeGen/GlobalISel/GenericMachineInstrs.h b/llvm/include/llvm/CodeGen/GlobalISel/GenericMachineInstrs.h index 1162134b2ad2..7103656365b1 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/GenericMachineInstrs.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/GenericMachineInstrs.h @@ -57,9 +57,9 @@ public: bool isUnordered() const { return getMMO().isUnordered(); } /// Returns the size in bytes of the memory access. - uint64_t getMemSize() { return getMMO().getSize(); + uint64_t getMemSize() const { return getMMO().getSize(); } /// Returns the size in bits of the memory access. - uint64_t getMemSizeInBits() { return getMMO().getSizeInBits(); } + uint64_t getMemSizeInBits() const { return getMMO().getSizeInBits(); } static bool classof(const MachineInstr *MI) { switch (MI->getOpcode()) { @@ -195,6 +195,37 @@ public: } }; +/// Represents a G_PTR_ADD. +class GPtrAdd : public GenericMachineInstr { +public: + Register getBaseReg() const { return getReg(1); } + Register getOffsetReg() const { return getReg(2); } + + static bool classof(const MachineInstr *MI) { + return MI->getOpcode() == TargetOpcode::G_PTR_ADD; + } +}; + +/// Represents a G_IMPLICIT_DEF. +class GImplicitDef : public GenericMachineInstr { +public: + static bool classof(const MachineInstr *MI) { + return MI->getOpcode() == TargetOpcode::G_IMPLICIT_DEF; + } +}; + +/// Represents a G_SELECT. +class GSelect : public GenericMachineInstr { +public: + Register getCondReg() const { return getReg(1); } + Register getTrueReg() const { return getReg(2); } + Register getFalseReg() const { return getReg(3); } + + static bool classof(const MachineInstr *MI) { + return MI->getOpcode() == TargetOpcode::G_SELECT; + } +}; + } // namespace llvm -#endif // LLVM_CODEGEN_GLOBALISEL_GENERICMACHINEINSTRS_H \ No newline at end of file +#endif // LLVM_CODEGEN_GLOBALISEL_GENERICMACHINEINSTRS_H diff --git a/llvm/include/llvm/CodeGen/GlobalISel/IRTranslator.h b/llvm/include/llvm/CodeGen/GlobalISel/IRTranslator.h index 8eab8a5846a7..ebe16cd4f58c 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/IRTranslator.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/IRTranslator.h @@ -20,6 +20,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/CodeGen/CodeGenCommonISel.h" #include "llvm/CodeGen/FunctionLoweringInfo.h" #include "llvm/CodeGen/GlobalISel/CSEMIRBuilder.h" #include "llvm/CodeGen/MachineFunctionPass.h" @@ -466,9 +467,8 @@ private: bool translateSIToFP(const User &U, MachineIRBuilder &MIRBuilder) { return translateCast(TargetOpcode::G_SITOFP, U, MIRBuilder); } - bool translateUnreachable(const User &U, MachineIRBuilder &MIRBuilder) { - return true; - } + bool translateUnreachable(const User &U, MachineIRBuilder &MIRBuilder); + bool translateSExt(const User &U, MachineIRBuilder &MIRBuilder) { return translateCast(TargetOpcode::G_SEXT, U, MIRBuilder); } @@ -586,6 +586,8 @@ private: /// stop translating such blocks early. bool HasTailCall = false; + StackProtectorDescriptor SPDescriptor; + /// Switch analysis and optimization. class GISelSwitchLowering : public SwitchCG::SwitchLowering { public: @@ -614,8 +616,34 @@ private: // * Clear the different maps. void finalizeFunction(); - // Handle emitting jump tables for each basic block. - void finalizeBasicBlock(); + // Processing steps done per block. E.g. emitting jump tables, stack + // protectors etc. Returns true if no errors, false if there was a problem + // that caused an abort. + bool finalizeBasicBlock(const BasicBlock &BB, MachineBasicBlock &MBB); + + /// Codegen a new tail for a stack protector check ParentMBB which has had its + /// tail spliced into a stack protector check success bb. + /// + /// For a high level explanation of how this fits into the stack protector + /// generation see the comment on the declaration of class + /// StackProtectorDescriptor. + /// + /// \return true if there were no problems. + bool emitSPDescriptorParent(StackProtectorDescriptor &SPD, + MachineBasicBlock *ParentBB); + + /// Codegen the failure basic block for a stack protector check. + /// + /// A failure stack protector machine basic block consists simply of a call to + /// __stack_chk_fail(). + /// + /// For a high level explanation of how this fits into the stack protector + /// generation see the comment on the declaration of class + /// StackProtectorDescriptor. + /// + /// \return true if there were no problems. + bool emitSPDescriptorFailure(StackProtectorDescriptor &SPD, + MachineBasicBlock *FailureBB); /// Get the VRegs that represent \p Val. /// Non-aggregate types have just one corresponding VReg and the list can be diff --git a/llvm/include/llvm/CodeGen/GlobalISel/LegacyLegalizerInfo.h b/llvm/include/llvm/CodeGen/GlobalISel/LegacyLegalizerInfo.h index b1f2103da309..f6704df3f49d 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/LegacyLegalizerInfo.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/LegacyLegalizerInfo.h @@ -478,4 +478,4 @@ private: } // end namespace llvm -#endif // define LLVM_CODEGEN_GLOBALISEL_LEGACYLEGALIZERINFO_H +#endif // LLVM_CODEGEN_GLOBALISEL_LEGACYLEGALIZERINFO_H diff --git a/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h b/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h index 44a48927d35a..8a603de2f91d 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h @@ -15,6 +15,7 @@ #define LLVM_CODEGEN_GLOBALISEL_LEGALIZATIONARTIFACTCOMBINER_H #include "llvm/ADT/SmallBitVector.h" +#include "llvm/CodeGen/GlobalISel/GISelChangeObserver.h" #include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h" #include "llvm/CodeGen/GlobalISel/Legalizer.h" #include "llvm/CodeGen/GlobalISel/LegalizerInfo.h" @@ -22,6 +23,7 @@ #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" #include "llvm/CodeGen/GlobalISel/Utils.h" #include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/Register.h" #include "llvm/Support/Debug.h" #define DEBUG_TYPE "legalizer" @@ -52,7 +54,8 @@ public: bool tryCombineAnyExt(MachineInstr &MI, SmallVectorImpl &DeadInsts, - SmallVectorImpl &UpdatedDefs) { + SmallVectorImpl &UpdatedDefs, + GISelObserverWrapper &Observer) { assert(MI.getOpcode() == TargetOpcode::G_ANYEXT); Builder.setInstrAndDebugLoc(MI); @@ -63,7 +66,11 @@ public: Register TruncSrc; if (mi_match(SrcReg, MRI, m_GTrunc(m_Reg(TruncSrc)))) { LLVM_DEBUG(dbgs() << ".. Combine MI: " << MI;); - Builder.buildAnyExtOrTrunc(DstReg, TruncSrc); + if (MRI.getType(DstReg) == MRI.getType(TruncSrc)) + replaceRegOrBuildCopy(DstReg, TruncSrc, MRI, Builder, UpdatedDefs, + Observer); + else + Builder.buildAnyExtOrTrunc(DstReg, TruncSrc); UpdatedDefs.push_back(DstReg); markInstAndDefDead(MI, *MRI.getVRegDef(SrcReg), DeadInsts); return true; @@ -120,12 +127,14 @@ public: return false; LLVM_DEBUG(dbgs() << ".. Combine MI: " << MI;); LLT SrcTy = MRI.getType(SrcReg); - APInt MaskVal = APInt::getAllOnesValue(SrcTy.getScalarSizeInBits()); + APInt MaskVal = APInt::getAllOnes(SrcTy.getScalarSizeInBits()); auto Mask = Builder.buildConstant( DstTy, MaskVal.zext(DstTy.getScalarSizeInBits())); - auto Extended = SextSrc ? Builder.buildSExtOrTrunc(DstTy, SextSrc) : - Builder.buildAnyExtOrTrunc(DstTy, TruncSrc); - Builder.buildAnd(DstReg, Extended, Mask); + if (SextSrc && (DstTy != MRI.getType(SextSrc))) + SextSrc = Builder.buildSExtOrTrunc(DstTy, SextSrc).getReg(0); + if (TruncSrc && (DstTy != MRI.getType(TruncSrc))) + TruncSrc = Builder.buildAnyExtOrTrunc(DstTy, TruncSrc).getReg(0); + Builder.buildAnd(DstReg, SextSrc ? SextSrc : TruncSrc, Mask); markInstAndDefDead(MI, *MRI.getVRegDef(SrcReg), DeadInsts); return true; } @@ -176,9 +185,9 @@ public: LLVM_DEBUG(dbgs() << ".. Combine MI: " << MI;); LLT SrcTy = MRI.getType(SrcReg); uint64_t SizeInBits = SrcTy.getScalarSizeInBits(); - Builder.buildInstr( - TargetOpcode::G_SEXT_INREG, {DstReg}, - {Builder.buildAnyExtOrTrunc(DstTy, TruncSrc), SizeInBits}); + if (DstTy != MRI.getType(TruncSrc)) + TruncSrc = Builder.buildAnyExtOrTrunc(DstTy, TruncSrc).getReg(0); + Builder.buildSExtInReg(DstReg, TruncSrc, SizeInBits); markInstAndDefDead(MI, *MRI.getVRegDef(SrcReg), DeadInsts); return true; } @@ -544,12 +553,14 @@ public: MachineIRBuilder &MIB; const LegalizerInfo &LI; - private: + // Stores the best register found in the current query so far. + Register CurrentBest = Register(); + /// Given an concat_vector op \p Concat and a start bit and size, try to /// find the origin of the value defined by that start position and size. /// - /// \returns A register if a value can be found, otherwise an empty - /// Register. + /// \returns a register with the requested size, or the current best + /// register found during the current query. Register findValueFromConcat(GConcatVectors &Concat, unsigned StartBit, unsigned Size) { assert(Size > 0); @@ -566,22 +577,22 @@ public: // FIXME: we might be able return multiple sources? Or create an // appropriate concat to make it fit. if (InRegOffset + Size > SrcSize) - return Register(); + return CurrentBest; - // If the bits exactly cover a single source, then return the operand as - // our value reg. Register SrcReg = Concat.getReg(StartSrcIdx); - if (InRegOffset == 0 && Size == SrcSize) - return SrcReg; // A source operand matches exactly. + if (InRegOffset == 0 && Size == SrcSize) { + CurrentBest = SrcReg; + return findValueFromDefImpl(SrcReg, 0, Size); + } - return findValueFromDef(SrcReg, InRegOffset, Size); + return findValueFromDefImpl(SrcReg, InRegOffset, Size); } /// Given an build_vector op \p BV and a start bit and size, try to find /// the origin of the value defined by that start position and size. /// - /// \returns A register if a value can be found, otherwise an empty - /// Register. + /// \returns a register with the requested size, or the current best + /// register found during the current query. Register findValueFromBuildVector(GBuildVector &BV, unsigned StartBit, unsigned Size) { assert(Size > 0); @@ -596,17 +607,21 @@ public: unsigned InRegOffset = StartBit % SrcSize; if (InRegOffset != 0) - return Register(); // Give up, bits don't start at a scalar source. + return CurrentBest; // Give up, bits don't start at a scalar source. if (Size < SrcSize) - return Register(); // Scalar source is too large for requested bits. + return CurrentBest; // Scalar source is too large for requested bits. // If the bits cover multiple sources evenly, then create a new // build_vector to synthesize the required size, if that's been requested. if (Size > SrcSize) { if (Size % SrcSize > 0) - return Register(); // Isn't covered exactly by sources. + return CurrentBest; // Isn't covered exactly by sources. unsigned NumSrcsUsed = Size / SrcSize; + // If we're requesting all of the sources, just return this def. + if (NumSrcsUsed == BV.getNumSources()) + return BV.getReg(0); + LLT SrcTy = MRI.getType(Src1Reg); LLT NewBVTy = LLT::fixed_vector(NumSrcsUsed, SrcTy); @@ -614,7 +629,7 @@ public: LegalizeActionStep ActionStep = LI.getAction({TargetOpcode::G_BUILD_VECTOR, {NewBVTy, SrcTy}}); if (ActionStep.Action != LegalizeActions::Legal) - return Register(); + return CurrentBest; SmallVector NewSrcs; for (unsigned SrcIdx = StartSrcIdx; SrcIdx < StartSrcIdx + NumSrcsUsed; @@ -630,8 +645,8 @@ public: /// Given an G_INSERT op \p MI and a start bit and size, try to find /// the origin of the value defined by that start position and size. /// - /// \returns A register if a value can be found, otherwise an empty - /// Register. + /// \returns a register with the requested size, or the current best + /// register found during the current query. Register findValueFromInsert(MachineInstr &MI, unsigned StartBit, unsigned Size) { assert(MI.getOpcode() == TargetOpcode::G_INSERT); @@ -685,28 +700,25 @@ public: if (EndBit <= InsertOffset || InsertedEndBit <= StartBit) { SrcRegToUse = ContainerSrcReg; NewStartBit = StartBit; - return findValueFromDef(SrcRegToUse, NewStartBit, Size); + return findValueFromDefImpl(SrcRegToUse, NewStartBit, Size); } if (InsertOffset <= StartBit && EndBit <= InsertedEndBit) { SrcRegToUse = InsertedReg; NewStartBit = StartBit - InsertOffset; - return findValueFromDef(SrcRegToUse, NewStartBit, Size); + if (NewStartBit == 0 && + Size == MRI.getType(SrcRegToUse).getSizeInBits()) + CurrentBest = SrcRegToUse; + return findValueFromDefImpl(SrcRegToUse, NewStartBit, Size); } // The bit range spans both the inserted and container regions. return Register(); } - public: - ArtifactValueFinder(MachineRegisterInfo &Mri, MachineIRBuilder &Builder, - const LegalizerInfo &Info) - : MRI(Mri), MIB(Builder), LI(Info) {} - - /// Try to find a source of the value defined in the def \p DefReg, starting - /// at position \p StartBit with size \p Size. - /// \returns an empty Register if no value could be found, or \p DefReg if - /// if that was the best we could do. - Register findValueFromDef(Register DefReg, unsigned StartBit, - unsigned Size) { + /// Internal implementation for findValueFromDef(). findValueFromDef() + /// initializes some data like the CurrentBest register, which this method + /// and its callees rely upon. + Register findValueFromDefImpl(Register DefReg, unsigned StartBit, + unsigned Size) { MachineInstr *Def = getDefIgnoringCopies(DefReg, MRI); // If the instruction has a single def, then simply delegate the search. // For unmerge however with multiple defs, we need to compute the offset @@ -724,7 +736,7 @@ public: } Register SrcReg = Def->getOperand(Def->getNumOperands() - 1).getReg(); Register SrcOriginReg = - findValueFromDef(SrcReg, StartBit + DefStartBit, Size); + findValueFromDefImpl(SrcReg, StartBit + DefStartBit, Size); if (SrcOriginReg) return SrcOriginReg; // Failed to find a further value. If the StartBit and Size perfectly @@ -732,7 +744,7 @@ public: // nothing. if (StartBit == 0 && Size == DefSize) return DefReg; - return Register(); + return CurrentBest; } case TargetOpcode::G_BUILD_VECTOR: return findValueFromBuildVector(cast(*Def), StartBit, @@ -740,41 +752,48 @@ public: case TargetOpcode::G_INSERT: return findValueFromInsert(*Def, StartBit, Size); default: - return Register(); + return CurrentBest; } } - }; - bool tryCombineUnmergeValues(GUnmerge &MI, - SmallVectorImpl &DeadInsts, - SmallVectorImpl &UpdatedDefs, - GISelChangeObserver &Observer) { - unsigned NumDefs = MI.getNumDefs(); - Register SrcReg = MI.getSourceReg(); - MachineInstr *SrcDef = getDefIgnoringCopies(SrcReg, MRI); - if (!SrcDef) - return false; - - LLT OpTy = MRI.getType(SrcReg); - LLT DestTy = MRI.getType(MI.getReg(0)); - unsigned SrcDefIdx = getDefIndex(*SrcDef, SrcReg); + public: + ArtifactValueFinder(MachineRegisterInfo &Mri, MachineIRBuilder &Builder, + const LegalizerInfo &Info) + : MRI(Mri), MIB(Builder), LI(Info) {} - Builder.setInstrAndDebugLoc(MI); + /// Try to find a source of the value defined in the def \p DefReg, starting + /// at position \p StartBit with size \p Size. + /// \returns a register with the requested size, or an empty Register if no + /// better value could be found. + Register findValueFromDef(Register DefReg, unsigned StartBit, + unsigned Size) { + CurrentBest = Register(); + Register FoundReg = findValueFromDefImpl(DefReg, StartBit, Size); + return FoundReg != DefReg ? FoundReg : Register(); + } - auto tryCombineViaValueFinder = [&]() { - ArtifactValueFinder ValueFinder(MRI, Builder, LI); + /// Try to combine the defs of an unmerge \p MI by attempting to find + /// values that provides the bits for each def reg. + /// \returns true if all the defs of the unmerge have been made dead. + bool tryCombineUnmergeDefs(GUnmerge &MI, GISelChangeObserver &Observer, + SmallVectorImpl &UpdatedDefs) { + unsigned NumDefs = MI.getNumDefs(); + LLT DestTy = MRI.getType(MI.getReg(0)); SmallBitVector DeadDefs(NumDefs); for (unsigned DefIdx = 0; DefIdx < NumDefs; ++DefIdx) { Register DefReg = MI.getReg(DefIdx); - Register FoundVal = - ValueFinder.findValueFromDef(DefReg, 0, DestTy.getSizeInBits()); - if (!FoundVal || FoundVal == DefReg) + if (MRI.use_nodbg_empty(DefReg)) { + DeadDefs[DefIdx] = true; + continue; + } + Register FoundVal = findValueFromDef(DefReg, 0, DestTy.getSizeInBits()); + if (!FoundVal) continue; if (MRI.getType(FoundVal) != DestTy) continue; - replaceRegOrBuildCopy(DefReg, FoundVal, MRI, Builder, UpdatedDefs, + replaceRegOrBuildCopy(DefReg, FoundVal, MRI, MIB, UpdatedDefs, Observer); // We only want to replace the uses, not the def of the old reg. Observer.changingInstr(MI); @@ -782,12 +801,31 @@ public: Observer.changedInstr(MI); DeadDefs[DefIdx] = true; } - if (DeadDefs.all()) { - markInstAndDefDead(MI, *SrcDef, DeadInsts, SrcDefIdx); - return true; - } + return DeadDefs.all(); + } + }; + + bool tryCombineUnmergeValues(GUnmerge &MI, + SmallVectorImpl &DeadInsts, + SmallVectorImpl &UpdatedDefs, + GISelChangeObserver &Observer) { + unsigned NumDefs = MI.getNumDefs(); + Register SrcReg = MI.getSourceReg(); + MachineInstr *SrcDef = getDefIgnoringCopies(SrcReg, MRI); + if (!SrcDef) return false; - }; + + LLT OpTy = MRI.getType(SrcReg); + LLT DestTy = MRI.getType(MI.getReg(0)); + unsigned SrcDefIdx = getDefIndex(*SrcDef, SrcReg); + + Builder.setInstrAndDebugLoc(MI); + + ArtifactValueFinder Finder(MRI, Builder, LI); + if (Finder.tryCombineUnmergeDefs(MI, Observer, UpdatedDefs)) { + markInstAndDefDead(MI, *SrcDef, DeadInsts, SrcDefIdx); + return true; + } if (auto *SrcUnmerge = dyn_cast(SrcDef)) { // %0:_(<4 x s16>) = G_FOO @@ -813,7 +851,7 @@ public: return false; break; default: - return tryCombineViaValueFinder(); + return false; } auto NewUnmerge = Builder.buildUnmerge(DestTy, SrcUnmergeSrc); @@ -845,11 +883,7 @@ public: ConvertOp, OpTy, DestTy)) { // We might have a chance to combine later by trying to combine // unmerge(cast) first - if (tryFoldUnmergeCast(MI, *SrcDef, DeadInsts, UpdatedDefs)) - return true; - - // Try using the value finder. - return tryCombineViaValueFinder(); + return tryFoldUnmergeCast(MI, *SrcDef, DeadInsts, UpdatedDefs); } const unsigned NumMergeRegs = MergeI->getNumOperands() - 1; @@ -1042,7 +1076,7 @@ public: default: return false; case TargetOpcode::G_ANYEXT: - Changed = tryCombineAnyExt(MI, DeadInsts, UpdatedDefs); + Changed = tryCombineAnyExt(MI, DeadInsts, UpdatedDefs, WrapperObserver); break; case TargetOpcode::G_ZEXT: Changed = tryCombineZExt(MI, DeadInsts, UpdatedDefs, WrapperObserver); diff --git a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h index 67141f3a6326..74615c73741a 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h @@ -256,6 +256,20 @@ private: LLT SrcTy, LLT NarrowTy, unsigned ScalarOpc); + // Memcpy family legalization helpers. + LegalizeResult lowerMemset(MachineInstr &MI, Register Dst, Register Val, + uint64_t KnownLen, Align Alignment, + bool IsVolatile); + LegalizeResult lowerMemcpyInline(MachineInstr &MI, Register Dst, Register Src, + uint64_t KnownLen, Align DstAlign, + Align SrcAlign, bool IsVolatile); + LegalizeResult lowerMemcpy(MachineInstr &MI, Register Dst, Register Src, + uint64_t KnownLen, uint64_t Limit, Align DstAlign, + Align SrcAlign, bool IsVolatile); + LegalizeResult lowerMemmove(MachineInstr &MI, Register Dst, Register Src, + uint64_t KnownLen, Align DstAlign, Align SrcAlign, + bool IsVolatile); + public: /// Return the alignment to use for a stack temporary object with the given /// type. @@ -402,6 +416,9 @@ public: LegalizeResult lowerDIVREM(MachineInstr &MI); LegalizeResult lowerAbsToAddXor(MachineInstr &MI); LegalizeResult lowerAbsToMaxNeg(MachineInstr &MI); + LegalizeResult lowerVectorReduction(MachineInstr &MI); + LegalizeResult lowerMemcpyInline(MachineInstr &MI); + LegalizeResult lowerMemCpyFamily(MachineInstr &MI, unsigned MaxLen = 0); }; /// Helper function that creates a libcall to the given \p Name using the given diff --git a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerInfo.h b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerInfo.h index 4fdfabbfb161..68c14240ebc7 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerInfo.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerInfo.h @@ -15,8 +15,6 @@ #define LLVM_CODEGEN_GLOBALISEL_LEGALIZERINFO_H #include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/None.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallBitVector.h" #include "llvm/ADT/SmallVector.h" @@ -113,6 +111,14 @@ struct LegalityQuery { LLT MemoryTy; uint64_t AlignInBits; AtomicOrdering Ordering; + + MemDesc() = default; + MemDesc(LLT MemoryTy, uint64_t AlignInBits, AtomicOrdering Ordering) + : MemoryTy(MemoryTy), AlignInBits(AlignInBits), Ordering(Ordering) {} + MemDesc(const MachineMemOperand &MMO) + : MemoryTy(MMO.getMemoryType()), + AlignInBits(MMO.getAlign().value() * 8), + Ordering(MMO.getSuccessOrdering()) {} }; /// Operations which require memory can use this to place requirements on the @@ -293,6 +299,10 @@ LegalityPredicate scalarOrEltNarrowerThan(unsigned TypeIdx, unsigned Size); /// type that's wider than the given size. LegalityPredicate scalarOrEltWiderThan(unsigned TypeIdx, unsigned Size); +/// True iff the specified type index is a scalar whose size is not a multiple +/// of Size. +LegalityPredicate sizeNotMultipleOf(unsigned TypeIdx, unsigned Size); + /// True iff the specified type index is a scalar whose size is not a power of /// 2. LegalityPredicate sizeNotPow2(unsigned TypeIdx); @@ -348,6 +358,11 @@ LegalizeMutation changeElementSizeTo(unsigned TypeIdx, unsigned FromTypeIdx); /// next power of 2. LegalizeMutation widenScalarOrEltToNextPow2(unsigned TypeIdx, unsigned Min = 0); +/// Widen the scalar type or vector element type for the given type index to +/// next multiple of \p Size. +LegalizeMutation widenScalarOrEltToNextMultipleOf(unsigned TypeIdx, + unsigned Size); + /// Add more elements to the type for the given type index to the next power of /// 2. LegalizeMutation moreElementsToNextPow2(unsigned TypeIdx, unsigned Min = 0); @@ -828,6 +843,16 @@ public: LegalizeMutations::widenScalarOrEltToNextPow2(TypeIdx, MinSize)); } + /// Widen the scalar to the next multiple of Size. No effect if the + /// type is not a scalar or is a multiple of Size. + LegalizeRuleSet &widenScalarToNextMultipleOf(unsigned TypeIdx, + unsigned Size) { + using namespace LegalityPredicates; + return actionIf( + LegalizeAction::WidenScalar, sizeNotMultipleOf(typeIdx(TypeIdx), Size), + LegalizeMutations::widenScalarOrEltToNextMultipleOf(TypeIdx, Size)); + } + /// Widen the scalar or vector element type to the next power of two that is /// at least MinSize. No effect if the scalar size is a power of two. LegalizeRuleSet &widenScalarOrEltToNextPow2(unsigned TypeIdx, diff --git a/llvm/include/llvm/CodeGen/GlobalISel/LoadStoreOpt.h b/llvm/include/llvm/CodeGen/GlobalISel/LoadStoreOpt.h new file mode 100644 index 000000000000..29575f386d7a --- /dev/null +++ b/llvm/include/llvm/CodeGen/GlobalISel/LoadStoreOpt.h @@ -0,0 +1,165 @@ +//== llvm/CodeGen/GlobalISel/LoadStoreOpt.h - LoadStoreOpt -------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +/// This is an optimization pass for GlobalISel generic memory operations. +/// Specifically, it focuses on merging stores and loads to consecutive +/// addresses. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CODEGEN_GLOBALISEL_LOADSTOREOPT_H +#define LLVM_CODEGEN_GLOBALISEL_LOADSTOREOPT_H + +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Analysis/AliasAnalysis.h" +#include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h" +#include "llvm/CodeGen/GlobalISel/LegalizerInfo.h" +#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" +#include "llvm/CodeGen/GlobalISel/Utils.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h" + +namespace llvm { +// Forward declarations. +class MachineRegisterInfo; +class TargetTransformInfo; +namespace GISelAddressing { +/// Helper struct to store a base, index and offset that forms an address +struct BaseIndexOffset { + Register BaseReg; + Register IndexReg; + int64_t Offset = 0; + bool IsIndexSignExt = false; +}; + +/// Returns a BaseIndexOffset which describes the pointer in \p Ptr. +BaseIndexOffset getPointerInfo(Register Ptr, MachineRegisterInfo &MRI); + +/// Compute whether or not a memory access at \p MI1 aliases with an access at +/// \p MI2 \returns true if either alias/no-alias is known. Sets \p IsAlias +/// accordingly. +bool aliasIsKnownForLoadStore(const MachineInstr &MI1, const MachineInstr &MI2, + bool &IsAlias, MachineRegisterInfo &MRI); + +/// Returns true if the instruction \p MI may alias \p Other. +/// This function uses multiple strategies to detect aliasing, whereas +/// aliasIsKnownForLoadStore just looks at the addresses of load/stores and is +/// tries to reason about base/index/offsets. +bool instMayAlias(const MachineInstr &MI, const MachineInstr &Other, + MachineRegisterInfo &MRI, AliasAnalysis *AA); +} // namespace GISelAddressing + +using namespace GISelAddressing; + +class LoadStoreOpt : public MachineFunctionPass { +public: + static char ID; + +private: + /// An input function to decide if the pass should run or not + /// on the given MachineFunction. + std::function DoNotRunPass; + + MachineRegisterInfo *MRI; + const TargetLowering *TLI; + MachineFunction *MF; + AliasAnalysis *AA; + const LegalizerInfo *LI; + + MachineIRBuilder Builder; + + /// Initialize the field members using \p MF. + void init(MachineFunction &MF); + + class StoreMergeCandidate { + public: + // The base pointer used as the base for all stores in this candidate. + Register BasePtr; + // Our algorithm is very simple at the moment. We assume that in instruction + // order stores are writing to incremeneting consecutive addresses. So when + // we walk the block in reverse order, the next eligible store must write to + // an offset one store width lower than CurrentLowestOffset. + uint64_t CurrentLowestOffset; + SmallVector Stores; + // A vector of MachineInstr/unsigned pairs to denote potential aliases that + // need to be checked before the candidate is considered safe to merge. The + // unsigned value is an index into the Stores vector. The indexed store is + // the highest-indexed store that has already been checked to not have an + // alias with the instruction. We record this so we don't have to repeat + // alias checks that have been already done, only those with stores added + // after the potential alias is recorded. + SmallVector> PotentialAliases; + + void addPotentialAlias(MachineInstr &MI); + + /// Reset this candidate back to an empty one. + void reset() { + Stores.clear(); + PotentialAliases.clear(); + CurrentLowestOffset = 0; + BasePtr = Register(); + } + }; + + bool isLegalOrBeforeLegalizer(const LegalityQuery &Query, + MachineFunction &MF) const; + /// If the given store is valid to be a member of the candidate, add it and + /// return true. Otherwise, returns false. + bool addStoreToCandidate(GStore &MI, StoreMergeCandidate &C); + /// Returns true if the instruction \p MI would potentially alias with any + /// stores in the candidate \p C. + bool operationAliasesWithCandidate(MachineInstr &MI, StoreMergeCandidate &C); + /// Merges the stores in the given vector into a wide store. + /// \p returns true if at least some of the stores were merged. + /// This may decide not to merge stores if heuristics predict it will not be + /// worth it. + bool mergeStores(SmallVectorImpl &StoresToMerge); + /// Perform a merge of all the stores in \p Stores into a single store. + /// Erases the old stores from the block when finished. + /// \returns true if merging was done. It may fail to perform a merge if + /// there are issues with materializing legal wide values. + bool doSingleStoreMerge(SmallVectorImpl &Stores); + bool processMergeCandidate(StoreMergeCandidate &C); + bool mergeBlockStores(MachineBasicBlock &MBB); + bool mergeFunctionStores(MachineFunction &MF); + + /// Initialize some target-specific data structures for the store merging + /// optimization. \p AddrSpace indicates which address space to use when + /// probing the legalizer info for legal stores. + void initializeStoreMergeTargetInfo(unsigned AddrSpace = 0); + /// A map between address space numbers and a bitvector of supported stores + /// sizes. Each bit in the bitvector represents whether a store size of + /// that bit's value is legal. E.g. if bit 64 is set, then 64 bit scalar + /// stores are legal. + DenseMap LegalStoreSizes; + bool IsPreLegalizer; + /// Contains instructions to be erased at the end of a block scan. + SmallSet InstsToErase; + +public: + LoadStoreOpt(); + LoadStoreOpt(std::function); + + StringRef getPassName() const override { return "LoadStoreOpt"; } + + MachineFunctionProperties getRequiredProperties() const override { + return MachineFunctionProperties() + .set(MachineFunctionProperties::Property::IsSSA); + } + + void getAnalysisUsage(AnalysisUsage &AU) const override; + + bool runOnMachineFunction(MachineFunction &MF) override; +}; + +} // End namespace llvm. + +#endif diff --git a/llvm/include/llvm/CodeGen/GlobalISel/MIPatternMatch.h b/llvm/include/llvm/CodeGen/GlobalISel/MIPatternMatch.h index 4c6b47ab9bc8..e813d030eec3 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/MIPatternMatch.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/MIPatternMatch.h @@ -63,7 +63,7 @@ struct ConstantMatch { int64_t &CR; ConstantMatch(int64_t &C) : CR(C) {} bool match(const MachineRegisterInfo &MRI, Register Reg) { - if (auto MaybeCst = getConstantVRegSExtVal(Reg, MRI)) { + if (auto MaybeCst = getIConstantVRegSExtVal(Reg, MRI)) { CR = *MaybeCst; return true; } @@ -73,21 +73,46 @@ struct ConstantMatch { inline ConstantMatch m_ICst(int64_t &Cst) { return ConstantMatch(Cst); } -struct ICstRegMatch { - Register &CR; - ICstRegMatch(Register &C) : CR(C) {} +struct GCstAndRegMatch { + Optional &ValReg; + GCstAndRegMatch(Optional &ValReg) : ValReg(ValReg) {} bool match(const MachineRegisterInfo &MRI, Register Reg) { - if (auto MaybeCst = getConstantVRegValWithLookThrough( - Reg, MRI, /*LookThroughInstrs*/ true, - /*HandleFConstants*/ false)) { - CR = MaybeCst->VReg; - return true; - } - return false; + ValReg = getIConstantVRegValWithLookThrough(Reg, MRI); + return ValReg ? true : false; } }; -inline ICstRegMatch m_ICst(Register &Reg) { return ICstRegMatch(Reg); } +inline GCstAndRegMatch m_GCst(Optional &ValReg) { + return GCstAndRegMatch(ValReg); +} + +struct GFCstAndRegMatch { + Optional &FPValReg; + GFCstAndRegMatch(Optional &FPValReg) : FPValReg(FPValReg) {} + bool match(const MachineRegisterInfo &MRI, Register Reg) { + FPValReg = getFConstantVRegValWithLookThrough(Reg, MRI); + return FPValReg ? true : false; + } +}; + +inline GFCstAndRegMatch m_GFCst(Optional &FPValReg) { + return GFCstAndRegMatch(FPValReg); +} + +struct GFCstOrSplatGFCstMatch { + Optional &FPValReg; + GFCstOrSplatGFCstMatch(Optional &FPValReg) + : FPValReg(FPValReg) {} + bool match(const MachineRegisterInfo &MRI, Register Reg) { + return (FPValReg = getFConstantSplat(Reg, MRI)) || + (FPValReg = getFConstantVRegValWithLookThrough(Reg, MRI)); + }; +}; + +inline GFCstOrSplatGFCstMatch +m_GFCstOrSplat(Optional &FPValReg) { + return GFCstOrSplatGFCstMatch(FPValReg); +} /// Matcher for a specific constant value. struct SpecificConstantMatch { diff --git a/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h b/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h index 9b652d8e16bc..069f71b54328 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h @@ -1537,6 +1537,14 @@ public: return buildInstr(TargetOpcode::G_XOR, {Dst}, {Src0, NegOne}); } + /// Build and insert integer negation + /// \p Zero = G_CONSTANT 0 + /// \p Res = G_SUB Zero, \p Op0 + MachineInstrBuilder buildNeg(const DstOp &Dst, const SrcOp &Src0) { + auto Zero = buildConstant(Dst.getLLTTy(*getMRI()), 0); + return buildInstr(TargetOpcode::G_SUB, {Dst}, {Zero, Src0}); + } + /// Build and insert \p Res = G_CTPOP \p Op0, \p Src0 MachineInstrBuilder buildCTPOP(const DstOp &Dst, const SrcOp &Src0) { return buildInstr(TargetOpcode::G_CTPOP, {Dst}, {Src0}); diff --git a/llvm/include/llvm/CodeGen/GlobalISel/Utils.h b/llvm/include/llvm/CodeGen/GlobalISel/Utils.h index 818475a48abb..86545b976b8d 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/Utils.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/Utils.h @@ -14,6 +14,9 @@ #ifndef LLVM_CODEGEN_GLOBALISEL_UTILS_H #define LLVM_CODEGEN_GLOBALISEL_UTILS_H +#include "GISelWorkList.h" +#include "LostDebugLocObserver.h" +#include "llvm/ADT/APFloat.h" #include "llvm/ADT/StringRef.h" #include "llvm/CodeGen/MachineBasicBlock.h" #include "llvm/CodeGen/Register.h" @@ -44,6 +47,7 @@ class TargetRegisterClass; class ConstantInt; class ConstantFP; class APFloat; +class MachineIRBuilder; // Convenience macros for dealing with vector reduction opcodes. #define GISEL_VECREDUCE_CASES_ALL \ @@ -162,13 +166,12 @@ void reportGISelWarning(MachineFunction &MF, const TargetPassConfig &TPC, MachineOptimizationRemarkMissed &R); /// If \p VReg is defined by a G_CONSTANT, return the corresponding value. -Optional getConstantVRegVal(Register VReg, - const MachineRegisterInfo &MRI); +Optional getIConstantVRegVal(Register VReg, + const MachineRegisterInfo &MRI); -/// If \p VReg is defined by a G_CONSTANT fits in int64_t -/// returns it. -Optional getConstantVRegSExtVal(Register VReg, - const MachineRegisterInfo &MRI); +/// If \p VReg is defined by a G_CONSTANT fits in int64_t returns it. +Optional getIConstantVRegSExtVal(Register VReg, + const MachineRegisterInfo &MRI); /// Simple struct used to hold a constant integer value and a virtual /// register. @@ -176,22 +179,32 @@ struct ValueAndVReg { APInt Value; Register VReg; }; -/// If \p VReg is defined by a statically evaluable chain of -/// instructions rooted on a G_F/CONSTANT (\p LookThroughInstrs == true) -/// and that constant fits in int64_t, returns its value as well as the -/// virtual register defined by this G_F/CONSTANT. -/// When \p LookThroughInstrs == false this function behaves like -/// getConstantVRegVal. -/// When \p HandleFConstants == false the function bails on G_FCONSTANTs. -/// When \p LookThroughAnyExt == true the function treats G_ANYEXT same as -/// G_SEXT. + +/// If \p VReg is defined by a statically evaluable chain of instructions rooted +/// on a G_CONSTANT returns its APInt value and def register. Optional -getConstantVRegValWithLookThrough(Register VReg, const MachineRegisterInfo &MRI, - bool LookThroughInstrs = true, - bool HandleFConstants = true, - bool LookThroughAnyExt = false); -const ConstantInt *getConstantIntVRegVal(Register VReg, - const MachineRegisterInfo &MRI); +getIConstantVRegValWithLookThrough(Register VReg, + const MachineRegisterInfo &MRI, + bool LookThroughInstrs = true); + +/// If \p VReg is defined by a statically evaluable chain of instructions rooted +/// on a G_CONSTANT or G_FCONSTANT returns its value as APInt and def register. +Optional getAnyConstantVRegValWithLookThrough( + Register VReg, const MachineRegisterInfo &MRI, + bool LookThroughInstrs = true, bool LookThroughAnyExt = false); + +struct FPValueAndVReg { + APFloat Value; + Register VReg; +}; + +/// If \p VReg is defined by a statically evaluable chain of instructions rooted +/// on a G_FCONSTANT returns its APFloat value and def register. +Optional +getFConstantVRegValWithLookThrough(Register VReg, + const MachineRegisterInfo &MRI, + bool LookThroughInstrs = true); + const ConstantFP* getConstantFPVRegVal(Register VReg, const MachineRegisterInfo &MRI); @@ -254,6 +267,14 @@ Optional ConstantFoldFPBinOp(unsigned Opcode, const Register Op1, const Register Op2, const MachineRegisterInfo &MRI); +/// Tries to constant fold a vector binop with sources \p Op1 and \p Op2. +/// If successful, returns the G_BUILD_VECTOR representing the folded vector +/// constant. \p MIB should have an insertion point already set to create new +/// G_CONSTANT instructions as needed. +Optional +ConstantFoldVectorBinop(unsigned Opcode, const Register Op1, const Register Op2, + const MachineRegisterInfo &MRI, MachineIRBuilder &MIB); + Optional ConstantFoldExtOp(unsigned Opcode, const Register Op1, uint64_t Imm, const MachineRegisterInfo &MRI); @@ -261,6 +282,11 @@ Optional ConstantFoldIntToFloat(unsigned Opcode, LLT DstTy, Register Src, const MachineRegisterInfo &MRI); +/// Tries to constant fold a G_CTLZ operation on \p Src. If \p Src is a vector +/// then it tries to do an element-wise constant fold. +Optional> +ConstantFoldCTLZ(Register Src, const MachineRegisterInfo &MRI); + /// Test if the given value is known to have exactly one bit set. This differs /// from computeKnownBits in that it doesn't necessarily determine which bit is /// set. @@ -346,15 +372,23 @@ Optional getSplatIndex(MachineInstr &MI); Optional getBuildVectorConstantSplat(const MachineInstr &MI, const MachineRegisterInfo &MRI); +/// Returns a floating point scalar constant of a build vector splat if it +/// exists. When \p AllowUndef == true some elements can be undef but not all. +Optional getFConstantSplat(Register VReg, + const MachineRegisterInfo &MRI, + bool AllowUndef = true); + /// Return true if the specified instruction is a G_BUILD_VECTOR or /// G_BUILD_VECTOR_TRUNC where all of the elements are 0 or undef. bool isBuildVectorAllZeros(const MachineInstr &MI, - const MachineRegisterInfo &MRI); + const MachineRegisterInfo &MRI, + bool AllowUndef = false); /// Return true if the specified instruction is a G_BUILD_VECTOR or /// G_BUILD_VECTOR_TRUNC where all of the elements are ~0 or undef. bool isBuildVectorAllOnes(const MachineInstr &MI, - const MachineRegisterInfo &MRI); + const MachineRegisterInfo &MRI, + bool AllowUndef = false); /// \returns a value when \p MI is a vector splat. The splat can be either a /// Register or a constant. @@ -378,6 +412,17 @@ bool isBuildVectorAllOnes(const MachineInstr &MI, Optional getVectorSplat(const MachineInstr &MI, const MachineRegisterInfo &MRI); +/// Determines if \p MI defines a constant integer or a build vector of +/// constant integers. Treats undef values as constants. +bool isConstantOrConstantVector(MachineInstr &MI, + const MachineRegisterInfo &MRI); + +/// Determines if \p MI defines a constant integer or a splat vector of +/// constant integers. +/// \returns the scalar constant or None. +Optional isConstantOrConstantSplatVector(MachineInstr &MI, + const MachineRegisterInfo &MRI); + /// Attempt to match a unary predicate against a scalar/splat constant or every /// element of a constant G_BUILD_VECTOR. If \p ConstVal is null, the source /// value was undef. @@ -398,5 +443,14 @@ int64_t getICmpTrueVal(const TargetLowering &TLI, bool IsVector, bool IsFP); bool shouldOptForSize(const MachineBasicBlock &MBB, ProfileSummaryInfo *PSI, BlockFrequencyInfo *BFI); +using SmallInstListTy = GISelWorkList<4>; +void saveUsesAndErase(MachineInstr &MI, MachineRegisterInfo &MRI, + LostDebugLocObserver *LocObserver, + SmallInstListTy &DeadInstChain); +void eraseInstrs(ArrayRef DeadInstrs, MachineRegisterInfo &MRI, + LostDebugLocObserver *LocObserver = nullptr); +void eraseInstr(MachineInstr &MI, MachineRegisterInfo &MRI, + LostDebugLocObserver *LocObserver = nullptr); + } // End namespace llvm. #endif diff --git a/llvm/include/llvm/CodeGen/ISDOpcodes.h b/llvm/include/llvm/CodeGen/ISDOpcodes.h index 6803f4d76cf0..fd106f55a43d 100644 --- a/llvm/include/llvm/CodeGen/ISDOpcodes.h +++ b/llvm/include/llvm/CodeGen/ISDOpcodes.h @@ -1260,6 +1260,11 @@ static const int FIRST_TARGET_STRICTFP_OPCODE = BUILTIN_OP_END + 400; /// be used with SelectionDAG::getMemIntrinsicNode. static const int FIRST_TARGET_MEMORY_OPCODE = BUILTIN_OP_END + 500; +/// Whether this is bitwise logic opcode. +inline bool isBitwiseLogicOp(unsigned Opcode) { + return Opcode == ISD::AND || Opcode == ISD::OR || Opcode == ISD::XOR; +} + /// Get underlying scalar opcode for VECREDUCE opcode. /// For example ISD::AND for ISD::VECREDUCE_AND. NodeType getVecReduceBaseOpcode(unsigned VecReduceOpcode); @@ -1267,6 +1272,12 @@ NodeType getVecReduceBaseOpcode(unsigned VecReduceOpcode); /// Whether this is a vector-predicated Opcode. bool isVPOpcode(unsigned Opcode); +/// Whether this is a vector-predicated binary operation opcode. +bool isVPBinaryOp(unsigned Opcode); + +/// Whether this is a vector-predicated reduction opcode. +bool isVPReduction(unsigned Opcode); + /// The operand position of the vector mask. Optional getVPMaskIdx(unsigned Opcode); diff --git a/llvm/include/llvm/CodeGen/IndirectThunks.h b/llvm/include/llvm/CodeGen/IndirectThunks.h index 74973f38bc79..90f9912f0ee0 100644 --- a/llvm/include/llvm/CodeGen/IndirectThunks.h +++ b/llvm/include/llvm/CodeGen/IndirectThunks.h @@ -62,7 +62,7 @@ void ThunkInserter::createThunkFunction(MachineModuleInfo &MMI, AttrBuilder B; B.addAttribute(llvm::Attribute::NoUnwind); B.addAttribute(llvm::Attribute::Naked); - F->addAttributes(llvm::AttributeList::FunctionIndex, B); + F->addFnAttrs(B); // Populate our function a bit so that we can verify. BasicBlock *Entry = BasicBlock::Create(Ctx, "entry", F); diff --git a/llvm/include/llvm/CodeGen/LinkAllAsmWriterComponents.h b/llvm/include/llvm/CodeGen/LinkAllAsmWriterComponents.h index 81b0025fdddc..c22f9d49f374 100644 --- a/llvm/include/llvm/CodeGen/LinkAllAsmWriterComponents.h +++ b/llvm/include/llvm/CodeGen/LinkAllAsmWriterComponents.h @@ -24,6 +24,9 @@ namespace { // delete it all as dead code, even with whole program optimization, // yet is effectively a NO-OP. As the compiler isn't smart enough // to know that getenv() never returns -1, this will do the job. + // This is so that globals in the translation units where these functions + // are defined are forced to be initialized, populating various + // registries. if (std::getenv("bar") != (char*) -1) return; diff --git a/llvm/include/llvm/CodeGen/LinkAllCodegenComponents.h b/llvm/include/llvm/CodeGen/LinkAllCodegenComponents.h index 1b13ff53ac85..d615a5db4504 100644 --- a/llvm/include/llvm/CodeGen/LinkAllCodegenComponents.h +++ b/llvm/include/llvm/CodeGen/LinkAllCodegenComponents.h @@ -27,6 +27,9 @@ namespace { // delete it all as dead code, even with whole program optimization, // yet is effectively a NO-OP. As the compiler isn't smart enough // to know that getenv() never returns -1, this will do the job. + // This is so that globals in the translation units where these functions + // are defined are forced to be initialized, populating various + // registries. if (std::getenv("bar") != (char*) -1) return; diff --git a/llvm/include/llvm/CodeGen/LiveInterval.h b/llvm/include/llvm/CodeGen/LiveInterval.h index c2b158ac1b7f..923a45821dd4 100644 --- a/llvm/include/llvm/CodeGen/LiveInterval.h +++ b/llvm/include/llvm/CodeGen/LiveInterval.h @@ -521,11 +521,11 @@ namespace llvm { removeSegment(S.start, S.end, RemoveDeadValNo); } - /// Remove segment pointed to by iterator @p I from this range. This does - /// not remove dead value numbers. - iterator removeSegment(iterator I) { - return segments.erase(I); - } + /// Remove segment pointed to by iterator @p I from this range. + iterator removeSegment(iterator I, bool RemoveDeadValNo = false); + + /// Mark \p ValNo for deletion if no segments in this range use it. + void removeValNoIfDead(VNInfo *ValNo); /// Query Liveness at Idx. /// The sub-instruction slot of Idx doesn't matter, only the instruction diff --git a/llvm/include/llvm/CodeGen/LiveIntervalUnion.h b/llvm/include/llvm/CodeGen/LiveIntervalUnion.h index 4ebe0f2dcfd8..3b6a4a379d72 100644 --- a/llvm/include/llvm/CodeGen/LiveIntervalUnion.h +++ b/llvm/include/llvm/CodeGen/LiveIntervalUnion.h @@ -114,12 +114,19 @@ public: const LiveRange *LR = nullptr; LiveRange::const_iterator LRI; ///< current position in LR ConstSegmentIter LiveUnionI; ///< current position in LiveUnion - Optional> InterferingVRegs; + SmallVector InterferingVRegs; bool CheckedFirstInterference = false; bool SeenAllInterferences = false; unsigned Tag = 0; unsigned UserTag = 0; + // Count the virtual registers in this union that interfere with this + // query's live virtual register, up to maxInterferingRegs. + unsigned collectInterferingVRegs(unsigned MaxInterferingRegs); + + // Was this virtual register visited during collectInterferingVRegs? + bool isSeenInterference(LiveInterval *VirtReg) const; + public: Query() = default; Query(const LiveRange &LR, const LiveIntervalUnion &LIU) @@ -131,7 +138,7 @@ public: const LiveIntervalUnion &NewLiveUnion) { LiveUnion = &NewLiveUnion; LR = &NewLR; - InterferingVRegs = None; + InterferingVRegs.clear(); CheckedFirstInterference = false; SeenAllInterferences = false; Tag = NewLiveUnion.getTag(); @@ -151,20 +158,12 @@ public: // Does this live virtual register interfere with the union? bool checkInterference() { return collectInterferingVRegs(1); } - // Count the virtual registers in this union that interfere with this - // query's live virtual register, up to maxInterferingRegs. - unsigned collectInterferingVRegs( - unsigned MaxInterferingRegs = std::numeric_limits::max()); - - // Was this virtual register visited during collectInterferingVRegs? - bool isSeenInterference(LiveInterval *VirtReg) const; - - // Did collectInterferingVRegs collect all interferences? - bool seenAllInterferences() const { return SeenAllInterferences; } - // Vector generated by collectInterferingVRegs. - const SmallVectorImpl &interferingVRegs() const { - return *InterferingVRegs; + const SmallVectorImpl &interferingVRegs( + unsigned MaxInterferingRegs = std::numeric_limits::max()) { + if (!SeenAllInterferences || MaxInterferingRegs < InterferingVRegs.size()) + collectInterferingVRegs(MaxInterferingRegs); + return InterferingVRegs; } }; diff --git a/llvm/include/llvm/CodeGen/LiveVariables.h b/llvm/include/llvm/CodeGen/LiveVariables.h index 9b0667bbbeb0..dee316677b25 100644 --- a/llvm/include/llvm/CodeGen/LiveVariables.h +++ b/llvm/include/llvm/CodeGen/LiveVariables.h @@ -188,6 +188,12 @@ public: //===--------------------------------------------------------------------===// // API to update live variable information + /// Recompute liveness from scratch for a virtual register \p Reg that is + /// known to have a single def that dominates all uses. This can be useful + /// after removing some uses of \p Reg. It is not necessary for the whole + /// machine function to be in SSA form. + void recomputeForSingleDefVirtReg(Register Reg); + /// replaceKillInstruction - Update register kill info by replacing a kill /// instruction with a new one. void replaceKillInstruction(Register Reg, MachineInstr &OldMI, diff --git a/llvm/include/llvm/CodeGen/LowLevelType.h b/llvm/include/llvm/CodeGen/LowLevelType.h index 40985e16b37a..922f93d2e598 100644 --- a/llvm/include/llvm/CodeGen/LowLevelType.h +++ b/llvm/include/llvm/CodeGen/LowLevelType.h @@ -16,8 +16,8 @@ #ifndef LLVM_CODEGEN_LOWLEVELTYPE_H #define LLVM_CODEGEN_LOWLEVELTYPE_H +#include "llvm/CodeGen/ValueTypes.h" #include "llvm/Support/LowLevelTypeImpl.h" -#include "llvm/Support/MachineValueType.h" namespace llvm { @@ -31,6 +31,7 @@ LLT getLLTForType(Type &Ty, const DataLayout &DL); /// Get a rough equivalent of an MVT for a given LLT. MVT can't distinguish /// pointers, so these will convert to a plain integer. MVT getMVTForLLT(LLT Ty); +EVT getApproximateEVTForLLT(LLT Ty, const DataLayout &DL, LLVMContext &Ctx); /// Get a rough equivalent of an LLT for a given MVT. LLT does not yet support /// scalarable vector types, and will assert if used. diff --git a/llvm/include/llvm/CodeGen/MIRFSDiscriminator.h b/llvm/include/llvm/CodeGen/MIRFSDiscriminator.h index 6137411b6dba..deb6b37a9bcf 100644 --- a/llvm/include/llvm/CodeGen/MIRFSDiscriminator.h +++ b/llvm/include/llvm/CodeGen/MIRFSDiscriminator.h @@ -57,6 +57,10 @@ public: assert(LowBit < HighBit && "HighBit needs to be greater than Lowbit"); } + StringRef getPassName() const override { + return "Add FS discriminators in MIR"; + } + /// getNumFSBBs() - Return the number of machine BBs that have FS samples. unsigned getNumFSBBs(); diff --git a/llvm/include/llvm/CodeGen/MIRFormatter.h b/llvm/include/llvm/CodeGen/MIRFormatter.h index 9cb92091db50..12c90600f6df 100644 --- a/llvm/include/llvm/CodeGen/MIRFormatter.h +++ b/llvm/include/llvm/CodeGen/MIRFormatter.h @@ -1,9 +1,8 @@ //===-- llvm/CodeGen/MIRFormatter.h -----------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/llvm/include/llvm/CodeGen/MIRSampleProfile.h b/llvm/include/llvm/CodeGen/MIRSampleProfile.h new file mode 100644 index 000000000000..2503524ccfdf --- /dev/null +++ b/llvm/include/llvm/CodeGen/MIRSampleProfile.h @@ -0,0 +1,76 @@ +//===----- MIRSampleProfile.h: SampleFDO Support in MIR ---*- c++ -*-------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains the supoorting functions for machine level Sample FDO +// loader. This is used in Flow Sensitive SampelFDO. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CODEGEN_MIRSAMPLEPROFILE_H +#define LLVM_CODEGEN_MIRSAMPLEPROFILE_H + +#include "llvm/Analysis/ProfileSummaryInfo.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineBlockFrequencyInfo.h" +#include "llvm/CodeGen/MachineBranchProbabilityInfo.h" +#include "llvm/CodeGen/MachineDominators.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineLoopInfo.h" +#include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h" +#include "llvm/CodeGen/MachinePostDominators.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Module.h" +#include "llvm/InitializePasses.h" +#include "llvm/ProfileData/InstrProf.h" +#include "llvm/ProfileData/SampleProf.h" +#include "llvm/ProfileData/SampleProfReader.h" + +#include + +namespace llvm { + +using namespace sampleprof; + +class MIRProfileLoader; +class MIRProfileLoaderPass : public MachineFunctionPass { + MachineFunction *MF; + std::string ProfileFileName; + FSDiscriminatorPass P; + unsigned LowBit; + unsigned HighBit; + +public: + static char ID; + /// FS bits will only use the '1' bits in the Mask. + MIRProfileLoaderPass(std::string FileName = "", + std::string RemappingFileName = "", + FSDiscriminatorPass P = FSDiscriminatorPass::Pass1); + + /// getMachineFunction - Return the last machine function computed. + const MachineFunction *getMachineFunction() const { return MF; } + + StringRef getPassName() const override { return "SampleFDO loader in MIR"; } + +private: + void init(MachineFunction &MF); + bool runOnMachineFunction(MachineFunction &) override; + bool doInitialization(Module &M) override; + void getAnalysisUsage(AnalysisUsage &AU) const override; + + std::unique_ptr MIRSampleLoader; + /// Hold the information of the basic block frequency. + MachineBlockFrequencyInfo *MBFI; +}; + +} // namespace llvm + +#endif // LLVM_CODEGEN_MIRSAMPLEPROFILE_H diff --git a/llvm/include/llvm/CodeGen/MIRYamlMapping.h b/llvm/include/llvm/CodeGen/MIRYamlMapping.h index e7428e7ad260..b6d7c2487126 100644 --- a/llvm/include/llvm/CodeGen/MIRYamlMapping.h +++ b/llvm/include/llvm/CodeGen/MIRYamlMapping.h @@ -694,6 +694,7 @@ struct MachineFunction { // Register information bool TracksRegLiveness = false; bool HasWinCFI = false; + bool FailsVerification = false; std::vector VirtualRegisters; std::vector LiveIns; Optional> CalleeSavedRegisters; @@ -722,6 +723,7 @@ template <> struct MappingTraits { YamlIO.mapOptional("failedISel", MF.FailedISel, false); YamlIO.mapOptional("tracksRegLiveness", MF.TracksRegLiveness, false); YamlIO.mapOptional("hasWinCFI", MF.HasWinCFI, false); + YamlIO.mapOptional("failsVerification", MF.FailsVerification, false); YamlIO.mapOptional("registers", MF.VirtualRegisters, std::vector()); YamlIO.mapOptional("liveins", MF.LiveIns, diff --git a/llvm/include/llvm/CodeGen/MachineCombinerPattern.h b/llvm/include/llvm/CodeGen/MachineCombinerPattern.h index ac0cc70744d1..67544779f34c 100644 --- a/llvm/include/llvm/CodeGen/MachineCombinerPattern.h +++ b/llvm/include/llvm/CodeGen/MachineCombinerPattern.h @@ -153,7 +153,18 @@ enum class MachineCombinerPattern { FMLSv4f32_OP1, FMLSv4f32_OP2, FMLSv4i32_indexed_OP1, - FMLSv4i32_indexed_OP2 + FMLSv4i32_indexed_OP2, + + FMULv2i32_indexed_OP1, + FMULv2i32_indexed_OP2, + FMULv2i64_indexed_OP1, + FMULv2i64_indexed_OP2, + FMULv4i16_indexed_OP1, + FMULv4i16_indexed_OP2, + FMULv4i32_indexed_OP1, + FMULv4i32_indexed_OP2, + FMULv8i16_indexed_OP1, + FMULv8i16_indexed_OP2, }; } // end namespace llvm diff --git a/llvm/include/llvm/CodeGen/MachineDominators.h b/llvm/include/llvm/CodeGen/MachineDominators.h index 46bf73cdd7b6..f749e9ff7e0a 100644 --- a/llvm/include/llvm/CodeGen/MachineDominators.h +++ b/llvm/include/llvm/CodeGen/MachineDominators.h @@ -36,6 +36,7 @@ extern template class DomTreeNodeBase; extern template class DominatorTreeBase; // DomTree extern template class DominatorTreeBase; // PostDomTree +using MachineDomTree = DomTreeBase; using MachineDomTreeNode = DomTreeNodeBase; //===------------------------------------- @@ -43,8 +44,6 @@ using MachineDomTreeNode = DomTreeNodeBase; /// compute a normal dominator tree. /// class MachineDominatorTree : public MachineFunctionPass { - using DomTreeT = DomTreeBase; - /// Helper structure used to hold all the basic blocks /// involved in the split of a critical edge. struct CriticalEdge { @@ -67,7 +66,7 @@ class MachineDominatorTree : public MachineFunctionPass { mutable SmallSet NewBBs; /// The DominatorTreeBase that is used to compute a normal dominator tree. - std::unique_ptr DT; + std::unique_ptr DT; /// Apply all the recorded critical edges to the DT. /// This updates the underlying DT information in a way that uses @@ -84,8 +83,9 @@ public: calculate(MF); } - DomTreeT &getBase() { - if (!DT) DT.reset(new DomTreeT()); + MachineDomTree &getBase() { + if (!DT) + DT.reset(new MachineDomTree()); applySplitCriticalEdges(); return *DT; } @@ -112,6 +112,12 @@ public: return DT->dominates(A, B); } + void getDescendants(MachineBasicBlock *A, + SmallVectorImpl &Result) { + applySplitCriticalEdges(); + DT->getDescendants(A, Result); + } + bool dominates(const MachineBasicBlock *A, const MachineBasicBlock *B) const { applySplitCriticalEdges(); return DT->dominates(A, B); diff --git a/llvm/include/llvm/CodeGen/MachineFrameInfo.h b/llvm/include/llvm/CodeGen/MachineFrameInfo.h index 28a59703dc60..5df468102a8a 100644 --- a/llvm/include/llvm/CodeGen/MachineFrameInfo.h +++ b/llvm/include/llvm/CodeGen/MachineFrameInfo.h @@ -342,6 +342,8 @@ public: : StackAlignment(assumeAligned(StackAlignment)), StackRealignable(StackRealignable), ForcedRealign(ForcedRealign) {} + MachineFrameInfo(const MachineFrameInfo &) = delete; + /// Return true if there are any stack objects in this function. bool hasStackObjects() const { return !Objects.empty(); } diff --git a/llvm/include/llvm/CodeGen/MachineFunction.h b/llvm/include/llvm/CodeGen/MachineFunction.h index 786fe908f68f..dcbd19ac6b5a 100644 --- a/llvm/include/llvm/CodeGen/MachineFunction.h +++ b/llvm/include/llvm/CodeGen/MachineFunction.h @@ -149,6 +149,9 @@ public: // all sizes attached to them have been eliminated. // TiedOpsRewritten: The twoaddressinstruction pass will set this flag, it // means that tied-def have been rewritten to meet the RegConstraint. + // FailsVerification: Means that the function is not expected to pass machine + // verification. This can be set by passes that introduce known problems that + // have not been fixed yet. enum class Property : unsigned { IsSSA, NoPHIs, @@ -159,7 +162,8 @@ public: RegBankSelected, Selected, TiedOpsRewritten, - LastProperty = TiedOpsRewritten, + FailsVerification, + LastProperty = FailsVerification, }; bool hasProperty(Property P) const { @@ -227,7 +231,7 @@ struct LandingPadInfo { : LandingPadBlock(MBB) {} }; -class MachineFunction { +class LLVM_EXTERNAL_VISIBILITY MachineFunction { Function &F; const LLVMTargetMachine &Target; const TargetSubtargetInfo *STI; @@ -536,6 +540,14 @@ public: /// (or DBG_PHI). void finalizeDebugInstrRefs(); + /// Returns true if the function's variable locations should be tracked with + /// instruction referencing. + bool useDebugInstrRef() const; + + /// A reserved operand number representing the instructions memory operand, + /// for instructions that have a stack spill fused into them. + const static unsigned int DebugOperandMemNumber; + MachineFunction(Function &F, const LLVMTargetMachine &Target, const TargetSubtargetInfo &STI, unsigned FunctionNum, MachineModuleInfo &MMI); diff --git a/llvm/include/llvm/CodeGen/MachineInstr.h b/llvm/include/llvm/CodeGen/MachineInstr.h index 757907f6d887..0ac934e208b6 100644 --- a/llvm/include/llvm/CodeGen/MachineInstr.h +++ b/llvm/include/llvm/CodeGen/MachineInstr.h @@ -517,7 +517,7 @@ public: SmallSet getUsedDebugRegs() const { assert(isDebugValue() && "not a DBG_VALUE*"); SmallSet UsedRegs; - for (auto MO : debug_operands()) + for (const auto &MO : debug_operands()) if (MO.isReg() && MO.getReg()) UsedRegs.insert(MO.getReg()); return UsedRegs; @@ -1331,6 +1331,7 @@ public: case TargetOpcode::LIFETIME_START: case TargetOpcode::LIFETIME_END: case TargetOpcode::PSEUDO_PROBE: + case TargetOpcode::ARITH_FENCE: return true; } } @@ -1859,17 +1860,6 @@ public: } } - PseudoProbeAttributes getPseudoProbeAttribute() const { - assert(isPseudoProbe() && "Must be a pseudo probe instruction"); - return (PseudoProbeAttributes)getOperand(3).getImm(); - } - - void addPseudoProbeAttribute(PseudoProbeAttributes Attr) { - assert(isPseudoProbe() && "Must be a pseudo probe instruction"); - MachineOperand &AttrOperand = getOperand(3); - AttrOperand.setImm(AttrOperand.getImm() | (uint32_t)Attr); - } - private: /// If this instruction is embedded into a MachineFunction, return the /// MachineRegisterInfo object for the current function, otherwise diff --git a/llvm/include/llvm/CodeGen/MachineMemOperand.h b/llvm/include/llvm/CodeGen/MachineMemOperand.h index 07b8e5ebcc1d..00080b171974 100644 --- a/llvm/include/llvm/CodeGen/MachineMemOperand.h +++ b/llvm/include/llvm/CodeGen/MachineMemOperand.h @@ -282,17 +282,7 @@ public: /// success and failure orderings for an atomic operation. (For operations /// other than cmpxchg, this is equivalent to getSuccessOrdering().) AtomicOrdering getMergedOrdering() const { - AtomicOrdering Ordering = getSuccessOrdering(); - AtomicOrdering FailureOrdering = getFailureOrdering(); - if (FailureOrdering == AtomicOrdering::SequentiallyConsistent) - return AtomicOrdering::SequentiallyConsistent; - if (FailureOrdering == AtomicOrdering::Acquire) { - if (Ordering == AtomicOrdering::Monotonic) - return AtomicOrdering::Acquire; - if (Ordering == AtomicOrdering::Release) - return AtomicOrdering::AcquireRelease; - } - return Ordering; + return getMergedAtomicOrdering(getSuccessOrdering(), getFailureOrdering()); } bool isLoad() const { return FlagVals & MOLoad; } diff --git a/llvm/include/llvm/CodeGen/MachineOptimizationRemarkEmitter.h b/llvm/include/llvm/CodeGen/MachineOptimizationRemarkEmitter.h index 8cc5909c40b7..285b858c96cb 100644 --- a/llvm/include/llvm/CodeGen/MachineOptimizationRemarkEmitter.h +++ b/llvm/include/llvm/CodeGen/MachineOptimizationRemarkEmitter.h @@ -118,6 +118,12 @@ public: : DiagnosticInfoMIROptimization(DK_MachineOptimizationRemarkAnalysis, PassName, RemarkName, Loc, MBB) {} + MachineOptimizationRemarkAnalysis(const char *PassName, StringRef RemarkName, + const MachineInstr *MI) + : DiagnosticInfoMIROptimization(DK_MachineOptimizationRemarkAnalysis, + PassName, RemarkName, MI->getDebugLoc(), + MI->getParent()) {} + static bool classof(const DiagnosticInfo *DI) { return DI->getKind() == DK_MachineOptimizationRemarkAnalysis; } diff --git a/llvm/include/llvm/CodeGen/MachineRegisterInfo.h b/llvm/include/llvm/CodeGen/MachineRegisterInfo.h index ca3dd992bbd5..dbabfe5f0f32 100644 --- a/llvm/include/llvm/CodeGen/MachineRegisterInfo.h +++ b/llvm/include/llvm/CodeGen/MachineRegisterInfo.h @@ -821,7 +821,7 @@ public: /// deleted during LiveDebugVariables analysis. void markUsesInDebugValueAsUndef(Register Reg) const; - /// updateDbgUsersToReg - Update a collection of DBG_VALUE instructions + /// updateDbgUsersToReg - Update a collection of debug instructions /// to refer to the designated register. void updateDbgUsersToReg(MCRegister OldReg, MCRegister NewReg, ArrayRef Users) const { @@ -829,21 +829,34 @@ public: for (MCRegUnitIterator RUI(OldReg, getTargetRegisterInfo()); RUI.isValid(); ++RUI) OldRegUnits.insert(*RUI); - for (MachineInstr *MI : Users) { - assert(MI->isDebugValue()); - for (auto &Op : MI->debug_operands()) { - if (Op.isReg()) { - for (MCRegUnitIterator RUI(OldReg, getTargetRegisterInfo()); - RUI.isValid(); ++RUI) { - if (OldRegUnits.contains(*RUI)) { - Op.setReg(NewReg); - break; - } + + // If this operand is a register, check whether it overlaps with OldReg. + // If it does, replace with NewReg. + auto UpdateOp = [this, &NewReg, &OldReg, &OldRegUnits](MachineOperand &Op) { + if (Op.isReg()) { + for (MCRegUnitIterator RUI(OldReg, getTargetRegisterInfo()); + RUI.isValid(); ++RUI) { + if (OldRegUnits.contains(*RUI)) { + Op.setReg(NewReg); + break; } } } - assert(MI->hasDebugOperandForReg(NewReg) && - "Expected debug value to have some overlap with OldReg"); + }; + + // Iterate through (possibly several) operands to DBG_VALUEs and update + // each. For DBG_PHIs, only one operand will be present. + for (MachineInstr *MI : Users) { + if (MI->isDebugValue()) { + for (auto &Op : MI->debug_operands()) + UpdateOp(Op); + assert(MI->hasDebugOperandForReg(NewReg) && + "Expected debug value to have some overlap with OldReg"); + } else if (MI->isDebugPHI()) { + UpdateOp(MI->getOperand(0)); + } else { + llvm_unreachable("Non-DBG_VALUE, Non-DBG_PHI debug instr updated"); + } } } @@ -964,7 +977,7 @@ public: MCRegister getLiveInPhysReg(Register VReg) const; /// getLiveInVirtReg - If PReg is a live-in physical register, return the - /// corresponding live-in physical register. + /// corresponding live-in virtual register. Register getLiveInVirtReg(MCRegister PReg) const; /// EmitLiveInCopies - Emit copies to initialize livein virtual registers diff --git a/llvm/include/llvm/CodeGen/MacroFusion.h b/llvm/include/llvm/CodeGen/MacroFusion.h index 3a140fe63fde..ea2c7a5faae3 100644 --- a/llvm/include/llvm/CodeGen/MacroFusion.h +++ b/llvm/include/llvm/CodeGen/MacroFusion.h @@ -23,6 +23,8 @@ class MachineInstr; class ScheduleDAGMutation; class TargetInstrInfo; class TargetSubtargetInfo; +class ScheduleDAGInstrs; +class SUnit; /// Check if the instr pair, FirstMI and SecondMI, should be fused /// together. Given SecondMI, when FirstMI is unspecified, then check if @@ -32,6 +34,18 @@ using ShouldSchedulePredTy = std::function; +/// Checks if the number of cluster edges between SU and its predecessors is +/// less than FuseLimit +bool hasLessThanNumFused(const SUnit &SU, unsigned FuseLimit); + +/// Create an artificial edge between FirstSU and SecondSU. +/// Make data dependencies from the FirstSU also dependent on the SecondSU to +/// prevent them from being scheduled between the FirstSU and the SecondSU +/// and vice-versa. +/// Fusing more than 2 instructions is not currently supported. +bool fuseInstructionPair(ScheduleDAGInstrs &DAG, SUnit &FirstSU, + SUnit &SecondSU); + /// Create a DAG scheduling mutation to pair instructions back to back /// for instructions that benefit according to the target-specific /// shouldScheduleAdjacent predicate function. diff --git a/llvm/include/llvm/CodeGen/Passes.h b/llvm/include/llvm/CodeGen/Passes.h index da1bab718948..d5ad12fadfa0 100644 --- a/llvm/include/llvm/CodeGen/Passes.h +++ b/llvm/include/llvm/CodeGen/Passes.h @@ -37,6 +37,10 @@ class raw_ostream; // List of target independent CodeGen pass IDs. namespace llvm { + + /// AtomicExpandPass - At IR level this pass replace atomic instructions with + /// __atomic_* library calls, or target specific instruction which implement the + /// same semantics in a way which better fits the target backend. FunctionPass *createAtomicExpandPass(); /// createUnreachableBlockEliminationPass - The LLVM code generator does not @@ -171,6 +175,9 @@ namespace llvm { /// This pass adds flow sensitive discriminators. extern char &MIRAddFSDiscriminatorsID; + /// This pass reads flow sensitive profile. + extern char &MIRProfileLoaderPassID; + /// FastRegisterAllocation Pass - This pass register allocates as fast as /// possible. It is best suited for debug code where live ranges are short. /// @@ -513,6 +520,11 @@ namespace llvm { FunctionPass * createMIRAddFSDiscriminatorsPass(sampleprof::FSDiscriminatorPass P); + /// Read Flow Sensitive Profile. + FunctionPass *createMIRProfileLoaderPass(std::string File, + std::string RemappingFile, + sampleprof::FSDiscriminatorPass P); + /// Creates MIR Debugify pass. \see MachineDebugify.cpp ModulePass *createDebugifyMachineModulePass(); diff --git a/llvm/include/llvm/CodeGen/RegAllocCommon.h b/llvm/include/llvm/CodeGen/RegAllocCommon.h index 39b77d919370..757ca8e112ee 100644 --- a/llvm/include/llvm/CodeGen/RegAllocCommon.h +++ b/llvm/include/llvm/CodeGen/RegAllocCommon.h @@ -1,9 +1,8 @@ //===- RegAllocCommon.h - Utilities shared between allocators ---*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/llvm/include/llvm/CodeGen/RegisterScavenging.h b/llvm/include/llvm/CodeGen/RegisterScavenging.h index 4f48ea2dc8e8..218e05f6eb6b 100644 --- a/llvm/include/llvm/CodeGen/RegisterScavenging.h +++ b/llvm/include/llvm/CodeGen/RegisterScavenging.h @@ -211,9 +211,6 @@ private: /// Initialize RegisterScavenger. void init(MachineBasicBlock &MBB); - /// Mark live-in registers of basic block as used. - void setLiveInsUsed(const MachineBasicBlock &MBB); - /// Spill a register after position \p After and reload it before position /// \p UseMI. ScavengedInfo &spill(Register Reg, const TargetRegisterClass &RC, int SPAdj, diff --git a/llvm/include/llvm/CodeGen/SelectionDAG.h b/llvm/include/llvm/CodeGen/SelectionDAG.h index 948a4763b872..5a3f4e9a23ff 100644 --- a/llvm/include/llvm/CodeGen/SelectionDAG.h +++ b/llvm/include/llvm/CodeGen/SelectionDAG.h @@ -531,7 +531,7 @@ public: } #ifndef NDEBUG - void VerifyDAGDiverence(); + void VerifyDAGDivergence(); #endif /// This iterates over the nodes in the SelectionDAG, folding @@ -621,8 +621,8 @@ public: SDValue getAllOnesConstant(const SDLoc &DL, EVT VT, bool IsTarget = false, bool IsOpaque = false) { - return getConstant(APInt::getAllOnesValue(VT.getScalarSizeInBits()), DL, - VT, IsTarget, IsOpaque); + return getConstant(APInt::getAllOnes(VT.getScalarSizeInBits()), DL, VT, + IsTarget, IsOpaque); } SDValue getConstant(const ConstantInt &Val, const SDLoc &DL, EVT VT, @@ -1307,6 +1307,74 @@ public: SDValue getIndexedStore(SDValue OrigStore, const SDLoc &dl, SDValue Base, SDValue Offset, ISD::MemIndexedMode AM); + SDValue getLoadVP(ISD::MemIndexedMode AM, ISD::LoadExtType ExtType, EVT VT, + const SDLoc &dl, SDValue Chain, SDValue Ptr, SDValue Offset, + SDValue Mask, SDValue EVL, MachinePointerInfo PtrInfo, + EVT MemVT, Align Alignment, + MachineMemOperand::Flags MMOFlags, const AAMDNodes &AAInfo, + const MDNode *Ranges = nullptr, bool IsExpanding = false); + inline SDValue + getLoadVP(ISD::MemIndexedMode AM, ISD::LoadExtType ExtType, EVT VT, + const SDLoc &dl, SDValue Chain, SDValue Ptr, SDValue Offset, + SDValue Mask, SDValue EVL, MachinePointerInfo PtrInfo, EVT MemVT, + MaybeAlign Alignment = MaybeAlign(), + MachineMemOperand::Flags MMOFlags = MachineMemOperand::MONone, + const AAMDNodes &AAInfo = AAMDNodes(), + const MDNode *Ranges = nullptr, bool IsExpanding = false) { + // Ensures that codegen never sees a None Alignment. + return getLoadVP(AM, ExtType, VT, dl, Chain, Ptr, Offset, Mask, EVL, + PtrInfo, MemVT, Alignment.getValueOr(getEVTAlign(MemVT)), + MMOFlags, AAInfo, Ranges, IsExpanding); + } + SDValue getLoadVP(ISD::MemIndexedMode AM, ISD::LoadExtType ExtType, EVT VT, + const SDLoc &dl, SDValue Chain, SDValue Ptr, SDValue Offset, + SDValue Mask, SDValue EVL, EVT MemVT, + MachineMemOperand *MMO, bool IsExpanding = false); + SDValue getLoadVP(EVT VT, const SDLoc &dl, SDValue Chain, SDValue Ptr, + SDValue Mask, SDValue EVL, MachinePointerInfo PtrInfo, + MaybeAlign Alignment, MachineMemOperand::Flags MMOFlags, + const AAMDNodes &AAInfo, const MDNode *Ranges = nullptr, + bool IsExpanding = false); + SDValue getLoadVP(EVT VT, const SDLoc &dl, SDValue Chain, SDValue Ptr, + SDValue Mask, SDValue EVL, MachineMemOperand *MMO, + bool IsExpanding = false); + SDValue getExtLoadVP(ISD::LoadExtType ExtType, const SDLoc &dl, EVT VT, + SDValue Chain, SDValue Ptr, SDValue Mask, SDValue EVL, + MachinePointerInfo PtrInfo, EVT MemVT, + MaybeAlign Alignment, MachineMemOperand::Flags MMOFlags, + const AAMDNodes &AAInfo, bool IsExpanding = false); + SDValue getExtLoadVP(ISD::LoadExtType ExtType, const SDLoc &dl, EVT VT, + SDValue Chain, SDValue Ptr, SDValue Mask, SDValue EVL, + EVT MemVT, MachineMemOperand *MMO, + bool IsExpanding = false); + SDValue getIndexedLoadVP(SDValue OrigLoad, const SDLoc &dl, SDValue Base, + SDValue Offset, ISD::MemIndexedMode AM); + SDValue getStoreVP(SDValue Chain, const SDLoc &dl, SDValue Val, SDValue Ptr, + SDValue Mask, SDValue EVL, MachinePointerInfo PtrInfo, + Align Alignment, MachineMemOperand::Flags MMOFlags, + const AAMDNodes &AAInfo = AAMDNodes(), + bool IsCompressing = false); + SDValue getStoreVP(SDValue Chain, const SDLoc &dl, SDValue Val, SDValue Ptr, + SDValue Mask, SDValue EVL, MachineMemOperand *MMO, + bool IsCompressing = false); + SDValue getTruncStoreVP(SDValue Chain, const SDLoc &dl, SDValue Val, + SDValue Ptr, SDValue Mask, SDValue EVL, + MachinePointerInfo PtrInfo, EVT SVT, Align Alignment, + MachineMemOperand::Flags MMOFlags, + const AAMDNodes &AAInfo, bool IsCompressing = false); + SDValue getTruncStoreVP(SDValue Chain, const SDLoc &dl, SDValue Val, + SDValue Ptr, SDValue Mask, SDValue EVL, EVT SVT, + MachineMemOperand *MMO, bool IsCompressing = false); + SDValue getIndexedStoreVP(SDValue OrigStore, const SDLoc &dl, SDValue Base, + SDValue Offset, ISD::MemIndexedMode AM); + + SDValue getGatherVP(SDVTList VTs, EVT VT, const SDLoc &dl, + ArrayRef Ops, MachineMemOperand *MMO, + ISD::MemIndexType IndexType); + SDValue getScatterVP(SDVTList VTs, EVT VT, const SDLoc &dl, + ArrayRef Ops, MachineMemOperand *MMO, + ISD::MemIndexType IndexType); + SDValue getMaskedLoad(EVT VT, const SDLoc &dl, SDValue Chain, SDValue Base, SDValue Offset, SDValue Mask, SDValue Src0, EVT MemVT, MachineMemOperand *MMO, ISD::MemIndexedMode AM, @@ -1664,10 +1732,6 @@ public: SDValue FoldConstantArithmetic(unsigned Opcode, const SDLoc &DL, EVT VT, ArrayRef Ops); - SDValue FoldConstantVectorArithmetic(unsigned Opcode, const SDLoc &DL, EVT VT, - ArrayRef Ops, - const SDNodeFlags Flags = SDNodeFlags()); - /// Fold floating-point operations with 2 operands when both operands are /// constants and/or undefined. SDValue foldConstantFPMath(unsigned Opcode, const SDLoc &DL, EVT VT, @@ -1769,6 +1833,19 @@ public: unsigned ComputeNumSignBits(SDValue Op, const APInt &DemandedElts, unsigned Depth = 0) const; + /// Get the minimum bit size for this Value \p Op as a signed integer. + /// i.e. x == sext(trunc(x to MinSignedBits) to bitwidth(x)). + /// Similar to the APInt::getMinSignedBits function. + /// Helper wrapper to ComputeNumSignBits. + unsigned ComputeMinSignedBits(SDValue Op, unsigned Depth = 0) const; + + /// Get the minimum bit size for this Value \p Op as a signed integer. + /// i.e. x == sext(trunc(x to MinSignedBits) to bitwidth(x)). + /// Similar to the APInt::getMinSignedBits function. + /// Helper wrapper to ComputeNumSignBits. + unsigned ComputeMinSignedBits(SDValue Op, const APInt &DemandedElts, + unsigned Depth = 0) const; + /// Return true if this function can prove that \p Op is never poison /// and, if \p PoisonOnly is false, does not have undef bits. bool isGuaranteedNotToBeUndefOrPoison(SDValue Op, bool PoisonOnly = false, diff --git a/llvm/include/llvm/CodeGen/SelectionDAGAddressAnalysis.h b/llvm/include/llvm/CodeGen/SelectionDAGAddressAnalysis.h index 4ee58333495b..6a3d76be0ed6 100644 --- a/llvm/include/llvm/CodeGen/SelectionDAGAddressAnalysis.h +++ b/llvm/include/llvm/CodeGen/SelectionDAGAddressAnalysis.h @@ -50,6 +50,7 @@ public: SDValue getIndex() { return Index; } SDValue getIndex() const { return Index; } bool hasValidOffset() const { return Offset.hasValue(); } + int64_t getOffset() const { return *Offset; } // Returns true if `Other` and `*this` are both some offset from the same base // pointer. In that case, `Off` is set to the offset between `*this` and diff --git a/llvm/include/llvm/CodeGen/SelectionDAGNodes.h b/llvm/include/llvm/CodeGen/SelectionDAGNodes.h index deeca98af3f3..2855e1f1e587 100644 --- a/llvm/include/llvm/CodeGen/SelectionDAGNodes.h +++ b/llvm/include/llvm/CodeGen/SelectionDAGNodes.h @@ -58,7 +58,6 @@ namespace llvm { class APInt; class Constant; -template struct DenseMapInfo; class GlobalValue; class MachineBasicBlock; class MachineConstantPoolValue; @@ -509,15 +508,19 @@ BEGIN_TWO_BYTE_PACK() class LSBaseSDNodeBitfields { friend class LSBaseSDNode; + friend class VPLoadStoreSDNode; friend class MaskedLoadStoreSDNode; friend class MaskedGatherScatterSDNode; + friend class VPGatherScatterSDNode; uint16_t : NumMemSDNodeBits; // This storage is shared between disparate class hierarchies to hold an // enumeration specific to the class hierarchy in use. // LSBaseSDNode => enum ISD::MemIndexedMode + // VPLoadStoreBaseSDNode => enum ISD::MemIndexedMode // MaskedLoadStoreBaseSDNode => enum ISD::MemIndexedMode + // VPGatherScatterSDNode => enum ISD::MemIndexType // MaskedGatherScatterSDNode => enum ISD::MemIndexType uint16_t AddressingMode : 3; }; @@ -525,8 +528,10 @@ BEGIN_TWO_BYTE_PACK() class LoadSDNodeBitfields { friend class LoadSDNode; + friend class VPLoadSDNode; friend class MaskedLoadSDNode; friend class MaskedGatherSDNode; + friend class VPGatherSDNode; uint16_t : NumLSBaseSDNodeBits; @@ -536,8 +541,10 @@ BEGIN_TWO_BYTE_PACK() class StoreSDNodeBitfields { friend class StoreSDNode; + friend class VPStoreSDNode; friend class MaskedStoreSDNode; friend class MaskedScatterSDNode; + friend class VPScatterSDNode; uint16_t : NumLSBaseSDNodeBits; @@ -1353,7 +1360,9 @@ public: const SDValue &getBasePtr() const { switch (getOpcode()) { case ISD::STORE: + case ISD::VP_STORE: case ISD::MSTORE: + case ISD::VP_SCATTER: return getOperand(2); case ISD::MGATHER: case ISD::MSCATTER: @@ -1393,6 +1402,10 @@ public: case ISD::MSTORE: case ISD::MGATHER: case ISD::MSCATTER: + case ISD::VP_LOAD: + case ISD::VP_STORE: + case ISD::VP_GATHER: + case ISD::VP_SCATTER: return true; default: return N->isMemIntrinsic() || N->isTargetMemoryOpcode(); @@ -1563,8 +1576,12 @@ public: Align getAlignValue() const { return Value->getAlignValue(); } bool isOne() const { return Value->isOne(); } - bool isNullValue() const { return Value->isZero(); } - bool isAllOnesValue() const { return Value->isMinusOne(); } + bool isZero() const { return Value->isZero(); } + // NOTE: This is soft-deprecated. Please use `isZero()` instead. + bool isNullValue() const { return isZero(); } + bool isAllOnes() const { return Value->isMinusOne(); } + // NOTE: This is soft-deprecated. Please use `isAllOnes()` instead. + bool isAllOnesValue() const { return isAllOnes(); } bool isMaxSignedValue() const { return Value->isMaxValue(true); } bool isMinSignedValue() const { return Value->isMinValue(true); } @@ -2031,8 +2048,25 @@ public: int32_t getConstantFPSplatPow2ToLog2Int(BitVector *UndefElements, uint32_t BitWidth) const; + /// Extract the raw bit data from a build vector of Undef, Constant or + /// ConstantFP node elements. Each raw bit element will be \p + /// DstEltSizeInBits wide, undef elements are treated as zero, and entirely + /// undefined elements are flagged in \p UndefElements. + bool getConstantRawBits(bool IsLittleEndian, unsigned DstEltSizeInBits, + SmallVectorImpl &RawBitElements, + BitVector &UndefElements) const; + bool isConstant() const; + /// Recast bit data \p SrcBitElements to \p DstEltSizeInBits wide elements. + /// Undef elements are treated as zero, and entirely undefined elements are + /// flagged in \p DstUndefElements. + static void recastRawBits(bool IsLittleEndian, unsigned DstEltSizeInBits, + SmallVectorImpl &DstBitElements, + ArrayRef SrcBitElements, + BitVector &DstUndefElements, + const BitVector &SrcUndefElements); + static bool classof(const SDNode *N) { return N->getOpcode() == ISD::BUILD_VECTOR; } @@ -2318,6 +2352,116 @@ public: } }; +/// This base class is used to represent VP_LOAD and VP_STORE nodes +class VPLoadStoreSDNode : public MemSDNode { +public: + friend class SelectionDAG; + + VPLoadStoreSDNode(ISD::NodeType NodeTy, unsigned Order, const DebugLoc &dl, + SDVTList VTs, ISD::MemIndexedMode AM, EVT MemVT, + MachineMemOperand *MMO) + : MemSDNode(NodeTy, Order, dl, VTs, MemVT, MMO) { + LSBaseSDNodeBits.AddressingMode = AM; + assert(getAddressingMode() == AM && "Value truncated"); + } + + // VPLoadSDNode (Chain, Ptr, Offset, Mask, EVL) + // VPStoreSDNode (Chain, Data, Ptr, Offset, Mask, EVL) + // Mask is a vector of i1 elements; + // the type of EVL is TLI.getVPExplicitVectorLengthTy(). + const SDValue &getOffset() const { + return getOperand(getOpcode() == ISD::VP_LOAD ? 2 : 3); + } + const SDValue &getBasePtr() const { + return getOperand(getOpcode() == ISD::VP_LOAD ? 1 : 2); + } + const SDValue &getMask() const { + return getOperand(getOpcode() == ISD::VP_LOAD ? 3 : 4); + } + const SDValue &getVectorLength() const { + return getOperand(getOpcode() == ISD::VP_LOAD ? 4 : 5); + } + + /// Return the addressing mode for this load or store: + /// unindexed, pre-inc, pre-dec, post-inc, or post-dec. + ISD::MemIndexedMode getAddressingMode() const { + return static_cast(LSBaseSDNodeBits.AddressingMode); + } + + /// Return true if this is a pre/post inc/dec load/store. + bool isIndexed() const { return getAddressingMode() != ISD::UNINDEXED; } + + /// Return true if this is NOT a pre/post inc/dec load/store. + bool isUnindexed() const { return getAddressingMode() == ISD::UNINDEXED; } + + static bool classof(const SDNode *N) { + return N->getOpcode() == ISD::VP_LOAD || N->getOpcode() == ISD::VP_STORE; + } +}; + +/// This class is used to represent a VP_LOAD node +class VPLoadSDNode : public VPLoadStoreSDNode { +public: + friend class SelectionDAG; + + VPLoadSDNode(unsigned Order, const DebugLoc &dl, SDVTList VTs, + ISD::MemIndexedMode AM, ISD::LoadExtType ETy, bool isExpanding, + EVT MemVT, MachineMemOperand *MMO) + : VPLoadStoreSDNode(ISD::VP_LOAD, Order, dl, VTs, AM, MemVT, MMO) { + LoadSDNodeBits.ExtTy = ETy; + LoadSDNodeBits.IsExpanding = isExpanding; + } + + ISD::LoadExtType getExtensionType() const { + return static_cast(LoadSDNodeBits.ExtTy); + } + + const SDValue &getBasePtr() const { return getOperand(1); } + const SDValue &getOffset() const { return getOperand(2); } + const SDValue &getMask() const { return getOperand(3); } + const SDValue &getVectorLength() const { return getOperand(4); } + + static bool classof(const SDNode *N) { + return N->getOpcode() == ISD::VP_LOAD; + } + bool isExpandingLoad() const { return LoadSDNodeBits.IsExpanding; } +}; + +/// This class is used to represent a VP_STORE node +class VPStoreSDNode : public VPLoadStoreSDNode { +public: + friend class SelectionDAG; + + VPStoreSDNode(unsigned Order, const DebugLoc &dl, SDVTList VTs, + ISD::MemIndexedMode AM, bool isTrunc, bool isCompressing, + EVT MemVT, MachineMemOperand *MMO) + : VPLoadStoreSDNode(ISD::VP_STORE, Order, dl, VTs, AM, MemVT, MMO) { + StoreSDNodeBits.IsTruncating = isTrunc; + StoreSDNodeBits.IsCompressing = isCompressing; + } + + /// Return true if this is a truncating store. + /// For integers this is the same as doing a TRUNCATE and storing the result. + /// For floats, it is the same as doing an FP_ROUND and storing the result. + bool isTruncatingStore() const { return StoreSDNodeBits.IsTruncating; } + + /// Returns true if the op does a compression to the vector before storing. + /// The node contiguously stores the active elements (integers or floats) + /// in src (those with their respective bit set in writemask k) to unaligned + /// memory at base_addr. + bool isCompressingStore() const { return StoreSDNodeBits.IsCompressing; } + + const SDValue &getValue() const { return getOperand(1); } + const SDValue &getBasePtr() const { return getOperand(2); } + const SDValue &getOffset() const { return getOperand(3); } + const SDValue &getMask() const { return getOperand(4); } + const SDValue &getVectorLength() const { return getOperand(5); } + + static bool classof(const SDNode *N) { + return N->getOpcode() == ISD::VP_STORE; + } +}; + /// This base class is used to represent MLOAD and MSTORE nodes class MaskedLoadStoreSDNode : public MemSDNode { public: @@ -2423,6 +2567,94 @@ public: } }; +/// This is a base class used to represent +/// VP_GATHER and VP_SCATTER nodes +/// +class VPGatherScatterSDNode : public MemSDNode { +public: + friend class SelectionDAG; + + VPGatherScatterSDNode(ISD::NodeType NodeTy, unsigned Order, + const DebugLoc &dl, SDVTList VTs, EVT MemVT, + MachineMemOperand *MMO, ISD::MemIndexType IndexType) + : MemSDNode(NodeTy, Order, dl, VTs, MemVT, MMO) { + LSBaseSDNodeBits.AddressingMode = IndexType; + assert(getIndexType() == IndexType && "Value truncated"); + } + + /// How is Index applied to BasePtr when computing addresses. + ISD::MemIndexType getIndexType() const { + return static_cast(LSBaseSDNodeBits.AddressingMode); + } + bool isIndexScaled() const { + return (getIndexType() == ISD::SIGNED_SCALED) || + (getIndexType() == ISD::UNSIGNED_SCALED); + } + bool isIndexSigned() const { + return (getIndexType() == ISD::SIGNED_SCALED) || + (getIndexType() == ISD::SIGNED_UNSCALED); + } + + // In the both nodes address is Op1, mask is Op2: + // VPGatherSDNode (Chain, base, index, scale, mask, vlen) + // VPScatterSDNode (Chain, value, base, index, scale, mask, vlen) + // Mask is a vector of i1 elements + const SDValue &getBasePtr() const { + return getOperand((getOpcode() == ISD::VP_GATHER) ? 1 : 2); + } + const SDValue &getIndex() const { + return getOperand((getOpcode() == ISD::VP_GATHER) ? 2 : 3); + } + const SDValue &getScale() const { + return getOperand((getOpcode() == ISD::VP_GATHER) ? 3 : 4); + } + const SDValue &getMask() const { + return getOperand((getOpcode() == ISD::VP_GATHER) ? 4 : 5); + } + const SDValue &getVectorLength() const { + return getOperand((getOpcode() == ISD::VP_GATHER) ? 5 : 6); + } + + static bool classof(const SDNode *N) { + return N->getOpcode() == ISD::VP_GATHER || + N->getOpcode() == ISD::VP_SCATTER; + } +}; + +/// This class is used to represent an VP_GATHER node +/// +class VPGatherSDNode : public VPGatherScatterSDNode { +public: + friend class SelectionDAG; + + VPGatherSDNode(unsigned Order, const DebugLoc &dl, SDVTList VTs, EVT MemVT, + MachineMemOperand *MMO, ISD::MemIndexType IndexType) + : VPGatherScatterSDNode(ISD::VP_GATHER, Order, dl, VTs, MemVT, MMO, + IndexType) {} + + static bool classof(const SDNode *N) { + return N->getOpcode() == ISD::VP_GATHER; + } +}; + +/// This class is used to represent an VP_SCATTER node +/// +class VPScatterSDNode : public VPGatherScatterSDNode { +public: + friend class SelectionDAG; + + VPScatterSDNode(unsigned Order, const DebugLoc &dl, SDVTList VTs, EVT MemVT, + MachineMemOperand *MMO, ISD::MemIndexType IndexType) + : VPGatherScatterSDNode(ISD::VP_SCATTER, Order, dl, VTs, MemVT, MMO, + IndexType) {} + + const SDValue &getValue() const { return getOperand(1); } + + static bool classof(const SDNode *N) { + return N->getOpcode() == ISD::VP_SCATTER; + } +}; + /// This is a base class used to represent /// MGATHER and MSCATTER nodes /// diff --git a/llvm/include/llvm/CodeGen/SwitchLoweringUtils.h b/llvm/include/llvm/CodeGen/SwitchLoweringUtils.h index 51f1d7d6fd21..bc22d7789856 100644 --- a/llvm/include/llvm/CodeGen/SwitchLoweringUtils.h +++ b/llvm/include/llvm/CodeGen/SwitchLoweringUtils.h @@ -183,12 +183,12 @@ struct JumpTableHeader { const Value *SValue; MachineBasicBlock *HeaderBB; bool Emitted; - bool OmitRangeCheck; + bool FallthroughUnreachable; JumpTableHeader(APInt F, APInt L, const Value *SV, MachineBasicBlock *H, bool E = false) : First(std::move(F)), Last(std::move(L)), SValue(SV), HeaderBB(H), - Emitted(E), OmitRangeCheck(false) {} + Emitted(E), FallthroughUnreachable(false) {} }; using JumpTableBlock = std::pair; @@ -218,14 +218,14 @@ struct BitTestBlock { BitTestInfo Cases; BranchProbability Prob; BranchProbability DefaultProb; - bool OmitRangeCheck; + bool FallthroughUnreachable; BitTestBlock(APInt F, APInt R, const Value *SV, unsigned Rg, MVT RgVT, bool E, bool CR, MachineBasicBlock *P, MachineBasicBlock *D, BitTestInfo C, BranchProbability Pr) : First(std::move(F)), Range(std::move(R)), SValue(SV), Reg(Rg), RegVT(RgVT), Emitted(E), ContiguousRange(CR), Parent(P), Default(D), - Cases(std::move(C)), Prob(Pr), OmitRangeCheck(false) {} + Cases(std::move(C)), Prob(Pr), FallthroughUnreachable(false) {} }; /// Return the range of values within a range. diff --git a/llvm/include/llvm/CodeGen/TargetCallingConv.h b/llvm/include/llvm/CodeGen/TargetCallingConv.h index 29e644898f6b..7713dd0800c0 100644 --- a/llvm/include/llvm/CodeGen/TargetCallingConv.h +++ b/llvm/include/llvm/CodeGen/TargetCallingConv.h @@ -247,11 +247,11 @@ namespace ISD { unsigned PartOffset; OutputArg() = default; - OutputArg(ArgFlagsTy flags, EVT vt, EVT argvt, bool isfixed, + OutputArg(ArgFlagsTy flags, MVT vt, EVT argvt, bool isfixed, unsigned origIdx, unsigned partOffs) - : Flags(flags), IsFixed(isfixed), OrigArgIndex(origIdx), - PartOffset(partOffs) { - VT = vt.getSimpleVT(); + : Flags(flags), IsFixed(isfixed), OrigArgIndex(origIdx), + PartOffset(partOffs) { + VT = vt; ArgVT = argvt; } }; diff --git a/llvm/include/llvm/CodeGen/TargetInstrInfo.h b/llvm/include/llvm/CodeGen/TargetInstrInfo.h index 05d0591f1e5d..8bc730a3eda5 100644 --- a/llvm/include/llvm/CodeGen/TargetInstrInfo.h +++ b/llvm/include/llvm/CodeGen/TargetInstrInfo.h @@ -411,9 +411,12 @@ public: /// This method returns a null pointer if the transformation cannot be /// performed, otherwise it returns the last new instruction. /// - virtual MachineInstr *convertToThreeAddress(MachineFunction::iterator &MFI, - MachineInstr &MI, - LiveVariables *LV) const { + /// If \p LIS is not nullptr, the LiveIntervals info should be updated for + /// replacing \p MI with new instructions, even though this function does not + /// remove MI. + virtual MachineInstr *convertToThreeAddress(MachineInstr &MI, + LiveVariables *LV, + LiveIntervals *LIS) const { return nullptr; } @@ -583,15 +586,14 @@ public: } /// Insert an unconditional indirect branch at the end of \p MBB to \p - /// NewDestBB. \p BrOffset indicates the offset of \p NewDestBB relative to + /// NewDestBB. Optionally, insert the clobbered register restoring in \p + /// RestoreBB. \p BrOffset indicates the offset of \p NewDestBB relative to /// the offset of the position to insert the new branch. - /// - /// \returns The number of bytes added to the block. - virtual unsigned insertIndirectBranch(MachineBasicBlock &MBB, - MachineBasicBlock &NewDestBB, - const DebugLoc &DL, - int64_t BrOffset = 0, - RegScavenger *RS = nullptr) const { + virtual void insertIndirectBranch(MachineBasicBlock &MBB, + MachineBasicBlock &NewDestBB, + MachineBasicBlock &RestoreBB, + const DebugLoc &DL, int64_t BrOffset = 0, + RegScavenger *RS = nullptr) const { llvm_unreachable("target did not implement"); } @@ -1537,7 +1539,8 @@ public: /// compares against in CmpValue. Return true if the comparison instruction /// can be analyzed. virtual bool analyzeCompare(const MachineInstr &MI, Register &SrcReg, - Register &SrcReg2, int &Mask, int &Value) const { + Register &SrcReg2, int64_t &Mask, + int64_t &Value) const { return false; } @@ -1545,7 +1548,8 @@ public: /// into something more efficient. E.g., on ARM most instructions can set the /// flags register, obviating the need for a separate CMP. virtual bool optimizeCompareInstr(MachineInstr &CmpInstr, Register SrcReg, - Register SrcReg2, int Mask, int Value, + Register SrcReg2, int64_t Mask, + int64_t Value, const MachineRegisterInfo *MRI) const { return false; } @@ -1624,9 +1628,6 @@ public: unsigned defaultDefLatency(const MCSchedModel &SchedModel, const MachineInstr &DefMI) const; - int computeDefOperandLatency(const InstrItineraryData *ItinData, - const MachineInstr &DefMI) const; - /// Return true if this opcode has high latency to its result. virtual bool isHighLatencyDef(int opc) const { return false; } diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h index 692dc4d7d4cf..87f5168ec48f 100644 --- a/llvm/include/llvm/CodeGen/TargetLowering.h +++ b/llvm/include/llvm/CodeGen/TargetLowering.h @@ -30,6 +30,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/CodeGen/DAGCombine.h" #include "llvm/CodeGen/ISDOpcodes.h" +#include "llvm/CodeGen/LowLevelType.h" #include "llvm/CodeGen/RuntimeLibcalls.h" #include "llvm/CodeGen/SelectionDAG.h" #include "llvm/CodeGen/SelectionDAGNodes.h" @@ -371,10 +372,18 @@ public: return getPointerTy(DL); } - /// EVT is not used in-tree, but is used by out-of-tree target. - /// A documentation for this function would be nice... + /// Return the type to use for a scalar shift opcode, given the shifted amount + /// type. Targets should return a legal type if the input type is legal. + /// Targets can return a type that is too small if the input type is illegal. virtual MVT getScalarShiftAmountTy(const DataLayout &, EVT) const; + /// Returns the type for the shift amount of a shift opcode. For vectors, + /// returns the input type. For scalars, behavior depends on \p LegalTypes. If + /// \p LegalTypes is true, calls getScalarShiftAmountTy, otherwise uses + /// pointer type. If getScalarShiftAmountTy or pointer type cannot represent + /// all possible shift amounts, returns MVT::i32. In general, \p LegalTypes + /// should be set to true for calls during type legalization and after type + /// legalization has been completed. EVT getShiftAmountTy(EVT LHSTy, const DataLayout &DL, bool LegalTypes = true) const; @@ -591,7 +600,7 @@ public: /// Returns if it's reasonable to merge stores to MemVT size. virtual bool canMergeStoresTo(unsigned AS, EVT MemVT, - const SelectionDAG &DAG) const { + const MachineFunction &MF) const { return true; } @@ -1396,6 +1405,11 @@ public: return NVT; } + virtual EVT getAsmOperandValueType(const DataLayout &DL, Type *Ty, + bool AllowUnknown = false) const { + return getValueType(DL, Ty, AllowUnknown); + } + /// Return the EVT corresponding to this LLVM type. This is fixed by the LLVM /// operations except for the pointer size. If AllowUnknown is true, this /// will return MVT::Other for types with no EVT counterpart (e.g. structs), @@ -1448,7 +1462,7 @@ public: /// Return the desired alignment for ByVal or InAlloca aggregate function /// arguments in the caller parameter area. This is the actual alignment, not /// its logarithm. - virtual unsigned getByValTypeAlignment(Type *Ty, const DataLayout &DL) const; + virtual uint64_t getByValTypeAlignment(Type *Ty, const DataLayout &DL) const; /// Return the type of registers that this ValueType will eventually require. MVT getRegisterType(MVT VT) const { @@ -1763,9 +1777,7 @@ public: Align getPrefFunctionAlignment() const { return PrefFunctionAlignment; } /// Return the preferred loop alignment. - virtual Align getPrefLoopAlignment(MachineLoop *ML = nullptr) const { - return PrefLoopAlignment; - } + virtual Align getPrefLoopAlignment(MachineLoop *ML = nullptr) const; /// Should loops be aligned even when the function is marked OptSize (but not /// MinSize). @@ -2077,6 +2089,20 @@ public: return false; } + /// Return true if it may be profitable to transform + /// (mul (add x, c1), c2) -> (add (mul x, c2), c1*c2). + /// This may not be true if c1 and c2 can be represented as immediates but + /// c1*c2 cannot, for example. + /// The target should check if c1, c2 and c1*c2 can be represented as + /// immediates, or have to be materialized into registers. If it is not sure + /// about some cases, a default true can be returned to let the DAGCombiner + /// decide. + /// AddNode is (add x, c1), and ConstNode is c2. + virtual bool isMulAddWithConstProfitable(const SDValue &AddNode, + const SDValue &ConstNode) const { + return true; + } + /// Return true if it is more correct/profitable to use strict FP_TO_INT /// conversion operations - canonicalizing the FP source value instead of /// converting all cases and then selecting based on value. @@ -2177,8 +2203,7 @@ protected: /// Indicate that the specified operation does not work with the specified /// type and indicate what to do about it. Note that VT may refer to either /// the type of a result or that of an operand of Op. - void setOperationAction(unsigned Op, MVT VT, - LegalizeAction Action) { + void setOperationAction(unsigned Op, MVT VT, LegalizeAction Action) { assert(Op < array_lengthof(OpActions[0]) && "Table isn't big enough!"); OpActions[(unsigned)VT.SimpleTy][Op] = Action; } @@ -2197,8 +2222,7 @@ protected: /// Indicate that the specified truncating store does not work with the /// specified type and indicate what to do about it. - void setTruncStoreAction(MVT ValVT, MVT MemVT, - LegalizeAction Action) { + void setTruncStoreAction(MVT ValVT, MVT MemVT, LegalizeAction Action) { assert(ValVT.isValid() && MemVT.isValid() && "Table isn't big enough!"); TruncStoreActions[(unsigned)ValVT.SimpleTy][MemVT.SimpleTy] = Action; } @@ -2506,8 +2530,11 @@ public: return false; } - virtual bool isTruncateFree(EVT FromVT, EVT ToVT) const { - return false; + virtual bool isTruncateFree(EVT FromVT, EVT ToVT) const { return false; } + virtual bool isTruncateFree(LLT FromTy, LLT ToTy, const DataLayout &DL, + LLVMContext &Ctx) const { + return isTruncateFree(getApproximateEVTForLLT(FromTy, DL, Ctx), + getApproximateEVTForLLT(ToTy, DL, Ctx)); } virtual bool isProfitableToHoist(Instruction *I) const { return true; } @@ -2583,8 +2610,11 @@ public: return false; } - virtual bool isZExtFree(EVT FromTy, EVT ToTy) const { - return false; + virtual bool isZExtFree(EVT FromTy, EVT ToTy) const { return false; } + virtual bool isZExtFree(LLT FromTy, LLT ToTy, const DataLayout &DL, + LLVMContext &Ctx) const { + return isZExtFree(getApproximateEVTForLLT(FromTy, DL, Ctx), + getApproximateEVTForLLT(ToTy, DL, Ctx)); } /// Return true if sign-extension from FromTy to ToTy is cheaper than @@ -3807,7 +3837,7 @@ public: RetSExt = Call.hasRetAttr(Attribute::SExt); RetZExt = Call.hasRetAttr(Attribute::ZExt); NoMerge = Call.hasFnAttr(Attribute::NoMerge); - + Callee = Target; CallConv = Call.getCallingConv(); @@ -4424,33 +4454,29 @@ public: /// Expand CTPOP nodes. Expands vector/scalar CTPOP nodes, /// vector nodes can only succeed if all operations are legal/custom. /// \param N Node to expand - /// \param Result output after conversion - /// \returns True, if the expansion was successful, false otherwise - bool expandCTPOP(SDNode *N, SDValue &Result, SelectionDAG &DAG) const; + /// \returns The expansion result or SDValue() if it fails. + SDValue expandCTPOP(SDNode *N, SelectionDAG &DAG) const; /// Expand CTLZ/CTLZ_ZERO_UNDEF nodes. Expands vector/scalar CTLZ nodes, /// vector nodes can only succeed if all operations are legal/custom. /// \param N Node to expand - /// \param Result output after conversion - /// \returns True, if the expansion was successful, false otherwise - bool expandCTLZ(SDNode *N, SDValue &Result, SelectionDAG &DAG) const; + /// \returns The expansion result or SDValue() if it fails. + SDValue expandCTLZ(SDNode *N, SelectionDAG &DAG) const; /// Expand CTTZ/CTTZ_ZERO_UNDEF nodes. Expands vector/scalar CTTZ nodes, /// vector nodes can only succeed if all operations are legal/custom. /// \param N Node to expand - /// \param Result output after conversion - /// \returns True, if the expansion was successful, false otherwise - bool expandCTTZ(SDNode *N, SDValue &Result, SelectionDAG &DAG) const; + /// \returns The expansion result or SDValue() if it fails. + SDValue expandCTTZ(SDNode *N, SelectionDAG &DAG) const; /// Expand ABS nodes. Expands vector/scalar ABS nodes, /// vector nodes can only succeed if all operations are legal/custom. /// (ABS x) -> (XOR (ADD x, (SRA x, type_size)), (SRA x, type_size)) /// \param N Node to expand - /// \param Result output after conversion /// \param IsNegative indicate negated abs - /// \returns True, if the expansion was successful, false otherwise - bool expandABS(SDNode *N, SDValue &Result, SelectionDAG &DAG, - bool IsNegative = false) const; + /// \returns The expansion result or SDValue() if it fails. + SDValue expandABS(SDNode *N, SelectionDAG &DAG, + bool IsNegative = false) const; /// Expand BSWAP nodes. Expands scalar/vector BSWAP nodes with i16/i32/i64 /// scalar types. Returns SDValue() if expand fails. diff --git a/llvm/include/llvm/CodeGen/TargetPassConfig.h b/llvm/include/llvm/CodeGen/TargetPassConfig.h index 11138039a3c5..9b13b61fc9de 100644 --- a/llvm/include/llvm/CodeGen/TargetPassConfig.h +++ b/llvm/include/llvm/CodeGen/TargetPassConfig.h @@ -187,8 +187,7 @@ public: void substitutePass(AnalysisID StandardID, IdentifyingPassPtr TargetID); /// Insert InsertedPassID pass after TargetPassID pass. - void insertPass(AnalysisID TargetPassID, IdentifyingPassPtr InsertedPassID, - bool VerifyAfter = true); + void insertPass(AnalysisID TargetPassID, IdentifyingPassPtr InsertedPassID); /// Allow the target to enable a specific standard pass by default. void enablePass(AnalysisID PassID) { substitutePass(PassID, PassID); } @@ -323,8 +322,7 @@ public: /// Add standard passes after a pass that has just been added. For example, /// the MachineVerifier if it is enabled. - void addMachinePostPasses(const std::string &Banner, bool AllowVerify = true, - bool AllowStrip = true); + void addMachinePostPasses(const std::string &Banner); /// Check whether or not GlobalISel should abort on error. /// When this is disabled, GlobalISel will fall back on SDISel instead of @@ -449,16 +447,12 @@ protected: /// Add a CodeGen pass at this point in the pipeline after checking overrides. /// Return the pass that was added, or zero if no pass was added. - /// @p verifyAfter if true and adding a machine function pass add an extra - /// machine verification pass afterwards. - AnalysisID addPass(AnalysisID PassID, bool verifyAfter = true); + AnalysisID addPass(AnalysisID PassID); /// Add a pass to the PassManager if that pass is supposed to be run, as /// determined by the StartAfter and StopAfter options. Takes ownership of the /// pass. - /// @p verifyAfter if true and adding a machine function pass add an extra - /// machine verification pass afterwards. - void addPass(Pass *P, bool verifyAfter = true); + void addPass(Pass *P); /// addMachinePasses helper to create the target-selected or overriden /// regalloc pass. diff --git a/llvm/include/llvm/CodeGen/TargetRegisterInfo.h b/llvm/include/llvm/CodeGen/TargetRegisterInfo.h index 92ce5b737090..8483d078ca74 100644 --- a/llvm/include/llvm/CodeGen/TargetRegisterInfo.h +++ b/llvm/include/llvm/CodeGen/TargetRegisterInfo.h @@ -57,6 +57,8 @@ public: /// Classes with a higher priority value are assigned first by register /// allocators using a greedy heuristic. The value is in the range [0,63]. const uint8_t AllocationPriority; + /// Configurable target specific flags. + const uint8_t TSFlags; /// Whether the class supports two (or more) disjunct subregister indices. const bool HasDisjunctSubRegs; /// Whether a combination of subregisters can cover every register in the @@ -871,10 +873,6 @@ public: /// (3) Bottom-up allocation is no longer guaranteed to optimally color. virtual bool reverseLocalAssignment() const { return false; } - /// Add the allocation priority to global and split ranges as well as the - /// local ranges when registers are added to the queue. - virtual bool addAllocPriorityToGlobalRanges() const { return false; } - /// Allow the target to override the cost of using a callee-saved register for /// the first time. Default value of 0 means we will use a callee-saved /// register if it is available. diff --git a/llvm/include/llvm/CodeGen/TargetSchedule.h b/llvm/include/llvm/CodeGen/TargetSchedule.h index aa6b82e14aa6..049ede89ab46 100644 --- a/llvm/include/llvm/CodeGen/TargetSchedule.h +++ b/llvm/include/llvm/CodeGen/TargetSchedule.h @@ -15,7 +15,6 @@ #ifndef LLVM_CODEGEN_TARGETSCHEDULE_H #define LLVM_CODEGEN_TARGETSCHEDULE_H -#include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/CodeGen/TargetSubtargetInfo.h" #include "llvm/Config/llvm-config.h" diff --git a/llvm/include/llvm/CodeGen/ValueTypes.td b/llvm/include/llvm/CodeGen/ValueTypes.td index 0e88e705e16b..7f989e08e9bf 100644 --- a/llvm/include/llvm/CodeGen/ValueTypes.td +++ b/llvm/include/llvm/CodeGen/ValueTypes.td @@ -216,6 +216,7 @@ def untyped : ValueType<8, 174>; // Produces an untyped value def funcref : ValueType<0, 175>; // WebAssembly's funcref type def externref : ValueType<0, 176>; // WebAssembly's externref type def x86amx : ValueType<8192, 177>; // X86 AMX value +def i64x8 : ValueType<512, 178>; // 8 Consecutive GPRs (AArch64) def token : ValueType<0, 248>; // TokenTy @@ -243,7 +244,7 @@ def Any : ValueType<0, 255>; /// This class is for targets that want to use pointer types in patterns /// with the GlobalISelEmitter. Targets must define their own pointer /// derived from this class. The scalar argument should be an -/// integer type with the same bit size as the ponter. +/// integer type with the same bit size as the pointer. /// e.g. def p0 : PtrValueType ; class PtrValueType : diff --git a/llvm/include/llvm/DWARFLinker/DWARFLinker.h b/llvm/include/llvm/DWARFLinker/DWARFLinker.h index 7b89c9f66f86..1c6d0b1ead86 100644 --- a/llvm/include/llvm/DWARFLinker/DWARFLinker.h +++ b/llvm/include/llvm/DWARFLinker/DWARFLinker.h @@ -80,7 +80,7 @@ public: CompileUnit::DIEInfo &Info) = 0; /// Apply the valid relocations to the buffer \p Data, taking into - /// account that Data is at \p BaseOffset in the debug_info section. + /// account that Data is at \p BaseOffset in the .debug_info section. /// /// \returns true whether any reloc has been applied. virtual bool applyValidRelocs(MutableArrayRef Data, uint64_t BaseOffset, @@ -109,7 +109,7 @@ public: /// Emit section named SecName with data SecData. virtual void emitSectionContents(StringRef SecData, StringRef SecName) = 0; - /// Emit the abbreviation table \p Abbrevs to the debug_abbrev section. + /// Emit the abbreviation table \p Abbrevs to the .debug_abbrev section. virtual void emitAbbrevs(const std::vector> &Abbrevs, unsigned DwarfVersion) = 0; @@ -137,7 +137,7 @@ public: virtual void emitAppleTypes(AccelTable &Table) = 0; - /// Emit debug_ranges for \p FuncRange by translating the + /// Emit .debug_ranges for \p FuncRange by translating the /// original \p Entries. virtual void emitRangesEntries( int64_t UnitPcOffset, uint64_t OrigLowPc, @@ -145,17 +145,17 @@ public: const std::vector &Entries, unsigned AddressSize) = 0; - /// Emit debug_aranges entries for \p Unit and if \p DoRangesSection is true, - /// also emit the debug_ranges entries for the DW_TAG_compile_unit's + /// Emit .debug_aranges entries for \p Unit and if \p DoRangesSection is true, + /// also emit the .debug_ranges entries for the DW_TAG_compile_unit's /// DW_AT_ranges attribute. virtual void emitUnitRangesEntries(CompileUnit &Unit, bool DoRangesSection) = 0; - /// Copy the debug_line over to the updated binary while unobfuscating the + /// Copy the .debug_line over to the updated binary while unobfuscating the /// file names and directories. virtual void translateLineTable(DataExtractor LineData, uint64_t Offset) = 0; - /// Emit the line table described in \p Rows into the debug_line section. + /// Emit the line table described in \p Rows into the .debug_line section. virtual void emitLineTableForUnit(MCDwarfLineTableParams Params, StringRef PrologueBytes, unsigned MinInstLength, @@ -175,7 +175,7 @@ public: virtual void emitFDE(uint32_t CIEOffset, uint32_t AddreSize, uint32_t Address, StringRef Bytes) = 0; - /// Emit the debug_loc contribution for \p Unit by copying the entries from + /// Emit the .debug_loc contribution for \p Unit by copying the entries from /// \p Dwarf and offsetting them. Update the location attributes to point to /// the new entries. virtual void emitLocationsForUnit( @@ -184,7 +184,7 @@ public: ProcessExpr) = 0; /// Emit the compilation unit header for \p Unit in the - /// debug_info section. + /// .debug_info section. /// /// As a side effect, this also switches the current Dwarf version /// of the MC layer to the one of U.getOrigUnit(). @@ -695,7 +695,7 @@ private: /// Assign an abbreviation number to \p Abbrev void assignAbbrev(DIEAbbrev &Abbrev); - /// Compute and emit debug_ranges section for \p Unit, and + /// Compute and emit .debug_ranges section for \p Unit, and /// patch the attributes referencing it. void patchRangesForUnit(const CompileUnit &Unit, DWARFContext &Dwarf, const DWARFFile &File) const; @@ -706,7 +706,7 @@ private: /// Extract the line tables from the original dwarf, extract the relevant /// parts according to the linked function ranges and emit the result in the - /// debug_line section. + /// .debug_line section. void patchLineTableForUnit(CompileUnit &Unit, DWARFContext &OrigDwarf, const DWARFFile &File); @@ -753,7 +753,7 @@ private: StringMap EmittedCIEs; /// Offset of the last CIE that has been emitted in the output - /// debug_frame section. + /// .debug_frame section. uint32_t LastCIEOffset = 0; /// Apple accelerator tables. diff --git a/llvm/include/llvm/DebugInfo/CodeView/CVRecord.h b/llvm/include/llvm/DebugInfo/CodeView/CVRecord.h index 18392e3608e7..99de8ebef812 100644 --- a/llvm/include/llvm/DebugInfo/CodeView/CVRecord.h +++ b/llvm/include/llvm/DebugInfo/CodeView/CVRecord.h @@ -10,7 +10,6 @@ #define LLVM_DEBUGINFO_CODEVIEW_CVRECORD_H #include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/Optional.h" #include "llvm/DebugInfo/CodeView/CodeView.h" #include "llvm/DebugInfo/CodeView/CodeViewError.h" #include "llvm/DebugInfo/CodeView/RecordSerialization.h" diff --git a/llvm/include/llvm/DebugInfo/CodeView/CodeViewRegisters.def b/llvm/include/llvm/DebugInfo/CodeView/CodeViewRegisters.def index 48ea7e52c172..4cee3abdde87 100644 --- a/llvm/include/llvm/DebugInfo/CodeView/CodeViewRegisters.def +++ b/llvm/include/llvm/DebugInfo/CodeView/CodeViewRegisters.def @@ -373,7 +373,7 @@ CV_REGISTER(AMD64_K7, 765) CV_REGISTER(ARM_NOREG, 0) -// General purpose 32-bit integer regisers +// General purpose 32-bit integer registers CV_REGISTER(ARM_R0, 10) CV_REGISTER(ARM_R1, 11) diff --git a/llvm/include/llvm/DebugInfo/CodeView/TypeIndex.h b/llvm/include/llvm/DebugInfo/CodeView/TypeIndex.h index bdc6cf46509b..226a436c0930 100644 --- a/llvm/include/llvm/DebugInfo/CodeView/TypeIndex.h +++ b/llvm/include/llvm/DebugInfo/CodeView/TypeIndex.h @@ -18,6 +18,7 @@ namespace llvm { class ScopedPrinter; +class StringRef; namespace codeview { diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h index dcb26f12b13e..cdf3f60f88be 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h @@ -144,6 +144,27 @@ public: const dwarf::Attribute Attr, const DWARFUnit &U) const; + /// Compute an offset from a DIE specified by DIE offset and attribute index. + /// + /// \param AttrIndex an index of DWARF attribute. + /// \param DIEOffset the DIE offset that points to the ULEB128 abbreviation + /// code in the .debug_info data. + /// \param U the DWARFUnit the contains the DIE. + /// \returns an offset of the attribute. + uint64_t getAttributeOffsetFromIndex(uint32_t AttrIndex, uint64_t DIEOffset, + const DWARFUnit &U) const; + + /// Extract a DWARF form value from a DIE speccified by attribute index and + /// its offset. + /// + /// \param AttrIndex an index of DWARF attribute. + /// \param Offset offset of the attribute. + /// \param U the DWARFUnit the contains the DIE. + /// \returns Optional DWARF form value if the attribute was extracted. + Optional + getAttributeValueFromOffset(uint32_t AttrIndex, uint64_t Offset, + const DWARFUnit &U) const; + bool extract(DataExtractor Data, uint64_t* OffsetPtr); void dump(raw_ostream &OS) const; diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFAddressRange.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFAddressRange.h index 154f7893aa17..537a03ec11fc 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFAddressRange.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFAddressRange.h @@ -39,6 +39,8 @@ struct DWARFAddressRange { /// Returns true if [LowPC, HighPC) intersects with [RHS.LowPC, RHS.HighPC). bool intersects(const DWARFAddressRange &RHS) const { assert(valid() && RHS.valid()); + if (SectionIndex != RHS.SectionIndex) + return false; // Empty ranges can't intersect. if (LowPC == HighPC || RHS.LowPC == RHS.HighPC) return false; @@ -69,12 +71,12 @@ struct DWARFAddressRange { inline bool operator<(const DWARFAddressRange &LHS, const DWARFAddressRange &RHS) { - return std::tie(LHS.LowPC, LHS.HighPC) < std::tie(RHS.LowPC, RHS.HighPC); + return std::tie(LHS.SectionIndex, LHS.LowPC, LHS.HighPC) < std::tie(RHS.SectionIndex, RHS.LowPC, RHS.HighPC); } inline bool operator==(const DWARFAddressRange &LHS, const DWARFAddressRange &RHS) { - return std::tie(LHS.LowPC, LHS.HighPC) == std::tie(RHS.LowPC, RHS.HighPC); + return std::tie(LHS.SectionIndex, LHS.LowPC, LHS.HighPC) == std::tie(RHS.SectionIndex, RHS.LowPC, RHS.HighPC); } raw_ostream &operator<<(raw_ostream &OS, const DWARFAddressRange &R); diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFContext.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFContext.h index 75b2280658f1..902973ff5722 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFContext.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFContext.h @@ -243,6 +243,7 @@ public: } DWARFCompileUnit *getDWOCompileUnitForHash(uint64_t Hash); + DWARFTypeUnit *getTypeUnitForHash(uint16_t Version, uint64_t Hash, bool IsDWO); /// Return the compile unit that includes an offset (relative to .debug_info). DWARFCompileUnit *getCompileUnitForOffset(uint64_t Offset); @@ -373,8 +374,24 @@ public: return {2, 4, 8}; } static bool isAddressSizeSupported(unsigned AddressSize) { - return llvm::any_of(getSupportedAddressSizes(), - [=](auto Elem) { return Elem == AddressSize; }); + return llvm::is_contained(getSupportedAddressSizes(), AddressSize); + } + template + static Error checkAddressSizeSupported(unsigned AddressSize, + std::error_code EC, char const *Fmt, + const Ts &...Vals) { + if (isAddressSizeSupported(AddressSize)) + return Error::success(); + std::string Buffer; + raw_string_ostream Stream(Buffer); + Stream << format(Fmt, Vals...) + << " has unsupported address size: " << AddressSize + << " (supported are "; + ListSeparator LS; + for (unsigned Size : DWARFContext::getSupportedAddressSizes()) + Stream << LS << Size; + Stream << ')'; + return make_error(Stream.str(), EC); } std::shared_ptr getDWOContext(StringRef AbsolutePath); @@ -387,9 +404,12 @@ public: function_ref getWarningHandler() { return WarningHandler; } + enum class ProcessDebugRelocations { Process, Ignore }; + static std::unique_ptr - create(const object::ObjectFile &Obj, const LoadedObjectInfo *L = nullptr, - std::string DWPName = "", + create(const object::ObjectFile &Obj, + ProcessDebugRelocations RelocAction = ProcessDebugRelocations::Process, + const LoadedObjectInfo *L = nullptr, std::string DWPName = "", std::function RecoverableErrorHandler = WithColor::defaultErrorHandler, std::function WarningHandler = diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h index 0bfe9f376f46..c4370cb54113 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h @@ -24,9 +24,11 @@ class DWARFDebugInfoEntry { /// Offset within the .debug_info of the start of this entry. uint64_t Offset = 0; - /// The integer depth of this DIE within the compile unit DIEs where the - /// compile/type unit DIE has a depth of zero. - uint32_t Depth = 0; + /// Index of the parent die. UINT32_MAX if there is no parent. + uint32_t ParentIdx = UINT32_MAX; + + /// Index of the sibling die. Zero if there is no sibling. + uint32_t SiblingIdx = 0; const DWARFAbbreviationDeclaration *AbbrevDecl = nullptr; @@ -36,15 +38,31 @@ public: /// Extracts a debug info entry, which is a child of a given unit, /// starting at a given offset. If DIE can't be extracted, returns false and /// doesn't change OffsetPtr. - bool extractFast(const DWARFUnit &U, uint64_t *OffsetPtr); - /// High performance extraction should use this call. bool extractFast(const DWARFUnit &U, uint64_t *OffsetPtr, const DWARFDataExtractor &DebugInfoData, uint64_t UEndOffset, - uint32_t Depth); + uint32_t ParentIdx); uint64_t getOffset() const { return Offset; } - uint32_t getDepth() const { return Depth; } + + /// Returns index of the parent die. + Optional getParentIdx() const { + if (ParentIdx == UINT32_MAX) + return None; + + return ParentIdx; + } + + /// Returns index of the sibling die. + Optional getSiblingIdx() const { + if (SiblingIdx == 0) + return None; + + return SiblingIdx; + } + + /// Set index of sibling. + void setSiblingIdx(uint32_t Idx) { SiblingIdx = Idx; } dwarf::Tag getTag() const { return AbbrevDecl ? AbbrevDecl->getTag() : dwarf::DW_TAG_null; diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h index d1d65372740b..ee15b6d4112d 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h @@ -110,10 +110,6 @@ public: /// Length of the prologue in bytes. uint64_t getLength() const; - int32_t getMaxLineIncrementForSpecialOpcode() const { - return LineBase + (int8_t)LineRange - 1; - } - /// Get DWARF-version aware access to the file name entry at the provided /// index. const llvm::DWARFDebugLine::FileNameEntry & diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugRangeList.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugRangeList.h index 2f72c642a2d5..0d9f37c5610b 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugRangeList.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugRangeList.h @@ -49,12 +49,7 @@ public: /// 2. An address, which defines the appropriate base address for /// use in interpreting the beginning and ending address offsets of /// subsequent entries of the location list. - bool isBaseAddressSelectionEntry(uint8_t AddressSize) const { - assert(AddressSize == 4 || AddressSize == 8); - if (AddressSize == 4) - return StartAddress == -1U; - return StartAddress == -1ULL; - } + bool isBaseAddressSelectionEntry(uint8_t AddressSize) const; }; private: diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h index 1903bab5e73f..8f93ebc4ebc0 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h @@ -182,6 +182,8 @@ public: DWARFDie getAttributeValueAsReferencedDie(dwarf::Attribute Attr) const; DWARFDie getAttributeValueAsReferencedDie(const DWARFFormValue &V) const; + DWARFDie resolveTypeUnitReference() const; + /// Extract the range base attribute from this DIE as absolute section offset. /// /// This is a utility function that checks for either the DW_AT_rnglists_base @@ -220,16 +222,6 @@ public: /// information is available. Expected getAddressRanges() const; - /// Get all address ranges for any DW_TAG_subprogram DIEs in this DIE or any - /// of its children. - /// - /// Get the hi/low PC range if both attributes are available or exrtracts the - /// non-contiguous address ranges from the DW_AT_ranges attribute for this DIE - /// and all children. - /// - /// \param Ranges the addres range vector to fill in. - void collectChildrenAddressRanges(DWARFAddressRangesVector &Ranges) const; - bool addressRangeContainsAddress(const uint64_t Address) const; Expected @@ -246,6 +238,8 @@ public: /// for ShortName if LinkageName is not found. /// Returns null if no name is found. const char *getName(DINameKind Kind) const; + void getFullName(raw_string_ostream &, + std::string *OriginalFullName = nullptr) const; /// Return the DIE short name resolving DW_AT_specification or /// DW_AT_abstract_origin references if necessary. Returns null if no name diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h index 794e859bfe72..b694eeacfd9d 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h @@ -86,24 +86,30 @@ public: uint64_t OperandEndOffsets[2]; public: - Description &getDescription() { return Desc; } - uint8_t getCode() { return Opcode; } - uint64_t getRawOperand(unsigned Idx) { return Operands[Idx]; } - uint64_t getOperandEndOffset(unsigned Idx) { return OperandEndOffsets[Idx]; } - uint64_t getEndOffset() { return EndOffset; } - bool extract(DataExtractor Data, uint8_t AddressSize, uint64_t Offset, - Optional Format); - bool isError() { return Error; } + const Description &getDescription() const { return Desc; } + uint8_t getCode() const { return Opcode; } + uint64_t getRawOperand(unsigned Idx) const { return Operands[Idx]; } + uint64_t getOperandEndOffset(unsigned Idx) const { + return OperandEndOffsets[Idx]; + } + uint64_t getEndOffset() const { return EndOffset; } + bool isError() const { return Error; } bool print(raw_ostream &OS, DIDumpOptions DumpOpts, const DWARFExpression *Expr, const MCRegisterInfo *RegInfo, - DWARFUnit *U, bool isEH); - bool verify(DWARFUnit *U); + DWARFUnit *U, bool isEH) const; + + /// Verify \p Op. Does not affect the return of \a isError(). + static bool verify(const Operation &Op, DWARFUnit *U); + + private: + bool extract(DataExtractor Data, uint8_t AddressSize, uint64_t Offset, + Optional Format); }; /// An iterator to go through the expression operations. class iterator : public iterator_facade_base { + const Operation> { friend class DWARFExpression; const DWARFExpression *Expr; uint64_t Offset; @@ -116,19 +122,17 @@ public: } public: - class Operation &operator++() { + iterator &operator++() { Offset = Op.isError() ? Expr->Data.getData().size() : Op.EndOffset; Op.Error = Offset >= Expr->Data.getData().size() || !Op.extract(Expr->Data, Expr->AddressSize, Offset, Expr->Format); - return Op; + return *this; } - class Operation &operator*() { - return Op; - } + const Operation &operator*() const { return Op; } - iterator skipBytes(uint64_t Add) { + iterator skipBytes(uint64_t Add) const { return iterator(Expr, Op.EndOffset + Add); } @@ -159,6 +163,8 @@ public: bool operator==(const DWARFExpression &RHS) const; + StringRef getData() const { return Data.getData(); } + private: DataExtractor Data; uint8_t AddressSize; diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFFormValue.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFFormValue.h index 43be024f0d37..3c051c3ea018 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFFormValue.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFFormValue.h @@ -102,10 +102,6 @@ public: return extractValue(Data, OffsetPtr, FormParams, nullptr, U); } - bool isInlinedCStr() const { - return Value.data != nullptr && Value.data == (const uint8_t *)Value.cstr; - } - /// getAsFoo functions below return the extracted value as Foo if only /// DWARFFormValue has form class is suitable for representing Foo. Optional getAsReference() const; @@ -123,6 +119,19 @@ public: Optional> getAsBlock() const; Optional getAsCStringOffset() const; Optional getAsReferenceUVal() const; + /// Correctly extract any file paths from a form value. + /// + /// These attributes can be in the from DW_AT_decl_file or DW_AT_call_file + /// attributes. We need to use the file index in the correct DWARFUnit's line + /// table prologue, and each DWARFFormValue has the DWARFUnit the form value + /// was extracted from. + /// + /// \param Kind The kind of path to extract. + /// + /// \returns A valid string value on success, or llvm::None if the form class + /// is not FC_Constant, or if the file index is not valid. + Optional + getAsFile(DILineInfoSpecifier::FileLineInfoKind Kind) const; /// Skip a form's value in \p DebugInfoData at the offset specified by /// \p OffsetPtr. diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h index 93d7e2b563fd..d471b80c7fe1 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h @@ -49,8 +49,6 @@ public: DieRangeInfo(std::vector Ranges) : Ranges(std::move(Ranges)) {} - typedef std::vector::const_iterator - address_range_iterator; typedef std::set::const_iterator die_range_info_iterator; /// Inserts the address range. If the range overlaps with an existing @@ -62,16 +60,6 @@ public: /// children address ranges must all be contained in. Optional insert(const DWARFAddressRange &R); - /// Finds an address range in the sorted vector of ranges. - address_range_iterator findRange(const DWARFAddressRange &R) const { - auto Begin = Ranges.begin(); - auto End = Ranges.end(); - auto Iter = std::upper_bound(Begin, End, R); - if (Iter != Begin) - --Iter; - return Iter; - } - /// Inserts the address range info. If any of its ranges overlaps with a /// range in an existing range info, the range info is *not* added and an /// iterator to the overlapping range info. @@ -91,14 +79,11 @@ private: raw_ostream &OS; DWARFContext &DCtx; DIDumpOptions DumpOpts; - /// A map that tracks all references (converted absolute references) so we - /// can verify each reference points to a valid DIE and not an offset that - /// lies between to valid DIEs. - std::map> ReferenceToDIEOffsets; uint32_t NumDebugLineErrors = 0; // Used to relax some checks that do not currently work portably bool IsObjectFile; bool IsMachOObject; + using ReferenceMap = std::map>; raw_ostream &error() const; raw_ostream &warn() const; @@ -140,6 +125,7 @@ private: bool verifyUnitHeader(const DWARFDataExtractor DebugInfoData, uint64_t *Offset, unsigned UnitIndex, uint8_t &UnitType, bool &isUnitDWARF64); + bool verifyName(const DWARFDie &Die); /// Verifies the header of a unit in a .debug_info or .debug_types section. /// @@ -156,7 +142,9 @@ private: /// \param Unit The DWARF Unit to verify. /// /// \returns The number of errors that occurred during verification. - unsigned verifyUnitContents(DWARFUnit &Unit); + unsigned verifyUnitContents(DWARFUnit &Unit, + ReferenceMap &UnitLocalReferences, + ReferenceMap &CrossUnitReferences); /// Verifies the unit headers and contents in a .debug_info or .debug_types /// section. @@ -208,7 +196,9 @@ private: /// /// \returns NumErrors The number of errors occurred during verification of /// attributes' forms in a unit - unsigned verifyDebugInfoForm(const DWARFDie &Die, DWARFAttribute &AttrValue); + unsigned verifyDebugInfoForm(const DWARFDie &Die, DWARFAttribute &AttrValue, + ReferenceMap &UnitLocalReferences, + ReferenceMap &CrossUnitReferences); /// Verifies the all valid references that were found when iterating through /// all of the DIE attributes. @@ -220,7 +210,9 @@ private: /// /// \returns NumErrors The number of errors occurred during verification of /// references for the .debug_info and .debug_types sections - unsigned verifyDebugInfoReferences(); + unsigned verifyDebugInfoReferences( + const ReferenceMap &, + llvm::function_ref GetUnitForDieOffset); /// Verify the DW_AT_stmt_list encoding and value and ensure that no /// compile units that have the same DW_AT_stmt_list value. diff --git a/llvm/include/llvm/DebugInfo/GSYM/StringTable.h b/llvm/include/llvm/DebugInfo/GSYM/StringTable.h index f7f800d01647..045c9e3f3ebd 100644 --- a/llvm/include/llvm/DebugInfo/GSYM/StringTable.h +++ b/llvm/include/llvm/DebugInfo/GSYM/StringTable.h @@ -9,7 +9,6 @@ #ifndef LLVM_DEBUGINFO_GSYM_STRINGTABLE_H #define LLVM_DEBUGINFO_GSYM_STRINGTABLE_H -#include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" #include "llvm/DebugInfo/GSYM/Range.h" #include diff --git a/llvm/include/llvm/DebugInfo/MSF/MSFCommon.h b/llvm/include/llvm/DebugInfo/MSF/MSFCommon.h index 83331b14b8af..a922839a999d 100644 --- a/llvm/include/llvm/DebugInfo/MSF/MSFCommon.h +++ b/llvm/include/llvm/DebugInfo/MSF/MSFCommon.h @@ -93,6 +93,9 @@ inline bool isValidBlockSize(uint32_t Size) { case 1024: case 2048: case 4096: + case 8192: + case 16384: + case 32768: return true; } return false; diff --git a/llvm/include/llvm/DebugInfo/MSF/MappedBlockStream.h b/llvm/include/llvm/DebugInfo/MSF/MappedBlockStream.h index 473c89e8106f..296a4840b779 100644 --- a/llvm/include/llvm/DebugInfo/MSF/MappedBlockStream.h +++ b/llvm/include/llvm/DebugInfo/MSF/MappedBlockStream.h @@ -58,12 +58,12 @@ public: return support::little; } - Error readBytes(uint32_t Offset, uint32_t Size, + Error readBytes(uint64_t Offset, uint64_t Size, ArrayRef &Buffer) override; - Error readLongestContiguousChunk(uint32_t Offset, + Error readLongestContiguousChunk(uint64_t Offset, ArrayRef &Buffer) override; - uint32_t getLength() override; + uint64_t getLength() override; BumpPtrAllocator &getAllocator() { return Allocator; } @@ -79,10 +79,10 @@ protected: private: const MSFStreamLayout &getStreamLayout() const { return StreamLayout; } - void fixCacheAfterWrite(uint32_t Offset, ArrayRef Data) const; + void fixCacheAfterWrite(uint64_t Offset, ArrayRef Data) const; - Error readBytes(uint32_t Offset, MutableArrayRef Buffer); - bool tryReadContiguously(uint32_t Offset, uint32_t Size, + Error readBytes(uint64_t Offset, MutableArrayRef Buffer); + bool tryReadContiguously(uint64_t Offset, uint64_t Size, ArrayRef &Buffer); const uint32_t BlockSize; @@ -125,13 +125,13 @@ public: return support::little; } - Error readBytes(uint32_t Offset, uint32_t Size, + Error readBytes(uint64_t Offset, uint64_t Size, ArrayRef &Buffer) override; - Error readLongestContiguousChunk(uint32_t Offset, + Error readLongestContiguousChunk(uint64_t Offset, ArrayRef &Buffer) override; - uint32_t getLength() override; + uint64_t getLength() override; - Error writeBytes(uint32_t Offset, ArrayRef Buffer) override; + Error writeBytes(uint64_t Offset, ArrayRef Buffer) override; Error commit() override; diff --git a/llvm/include/llvm/DebugInfo/PDB/Native/DbiModuleList.h b/llvm/include/llvm/DebugInfo/PDB/Native/DbiModuleList.h index 5fb13ad30e91..de5b46f21672 100644 --- a/llvm/include/llvm/DebugInfo/PDB/Native/DbiModuleList.h +++ b/llvm/include/llvm/DebugInfo/PDB/Native/DbiModuleList.h @@ -31,9 +31,7 @@ struct FileInfoSubstreamHeader; class DbiModuleSourceFilesIterator : public iterator_facade_base { - using BaseType = - iterator_facade_base; + using BaseType = typename DbiModuleSourceFilesIterator::iterator_facade_base; public: DbiModuleSourceFilesIterator(const DbiModuleList &Modules, uint32_t Modi, diff --git a/llvm/include/llvm/DebugInfo/PDB/Native/HashTable.h b/llvm/include/llvm/DebugInfo/PDB/Native/HashTable.h index 95c0a89551ed..474bd796b2b3 100644 --- a/llvm/include/llvm/DebugInfo/PDB/Native/HashTable.h +++ b/llvm/include/llvm/DebugInfo/PDB/Native/HashTable.h @@ -38,6 +38,7 @@ class HashTableIterator : public iterator_facade_base, std::forward_iterator_tag, const std::pair> { + using BaseT = typename HashTableIterator::iterator_facade_base; friend HashTable; HashTableIterator(const HashTable &Map, uint32_t Index, @@ -76,9 +77,7 @@ public: // Implement postfix op++ in terms of prefix op++ by using the superclass // implementation. - using iterator_facade_base, - std::forward_iterator_tag, - const std::pair>::operator++; + using BaseT::operator++; HashTableIterator &operator++() { while (Index < Map->Buckets.size()) { ++Index; diff --git a/llvm/include/llvm/DebugInfo/PDB/Native/NamedStreamMap.h b/llvm/include/llvm/DebugInfo/PDB/Native/NamedStreamMap.h index 1df059ffa9fd..f110e90b3f90 100644 --- a/llvm/include/llvm/DebugInfo/PDB/Native/NamedStreamMap.h +++ b/llvm/include/llvm/DebugInfo/PDB/Native/NamedStreamMap.h @@ -9,7 +9,6 @@ #ifndef LLVM_DEBUGINFO_PDB_NATIVE_NAMEDSTREAMMAP_H #define LLVM_DEBUGINFO_PDB_NATIVE_NAMEDSTREAMMAP_H -#include "llvm/ADT/Optional.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/iterator_range.h" diff --git a/llvm/include/llvm/DebugInfo/PDB/Native/NativeLineNumber.h b/llvm/include/llvm/DebugInfo/PDB/Native/NativeLineNumber.h index 5dedc70f11ba..be0ddf0a063a 100644 --- a/llvm/include/llvm/DebugInfo/PDB/Native/NativeLineNumber.h +++ b/llvm/include/llvm/DebugInfo/PDB/Native/NativeLineNumber.h @@ -9,7 +9,6 @@ #ifndef LLVM_DEBUGINFO_PDB_NATIVE_NATIVELINENUMBER_H #define LLVM_DEBUGINFO_PDB_NATIVE_NATIVELINENUMBER_H -#include "llvm/ADT/Optional.h" #include "llvm/DebugInfo/CodeView/Line.h" #include "llvm/DebugInfo/PDB/IPDBLineNumber.h" #include "llvm/DebugInfo/PDB/Native/NativeSession.h" diff --git a/llvm/include/llvm/DebugInfo/PDB/Native/NativeTypeFunctionSig.h b/llvm/include/llvm/DebugInfo/PDB/Native/NativeTypeFunctionSig.h index 8f1834d0a2c2..90b5d8068959 100644 --- a/llvm/include/llvm/DebugInfo/PDB/Native/NativeTypeFunctionSig.h +++ b/llvm/include/llvm/DebugInfo/PDB/Native/NativeTypeFunctionSig.h @@ -9,7 +9,6 @@ #ifndef LLVM_DEBUGINFO_PDB_NATIVE_NATIVETYPEFUNCTIONSIG_H #define LLVM_DEBUGINFO_PDB_NATIVE_NATIVETYPEFUNCTIONSIG_H -#include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/DebugInfo/CodeView/CodeView.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" diff --git a/llvm/include/llvm/DebugInfo/PDB/Native/NativeTypeVTShape.h b/llvm/include/llvm/DebugInfo/PDB/Native/NativeTypeVTShape.h index 4ae8f1471781..21995ca665c1 100644 --- a/llvm/include/llvm/DebugInfo/PDB/Native/NativeTypeVTShape.h +++ b/llvm/include/llvm/DebugInfo/PDB/Native/NativeTypeVTShape.h @@ -9,7 +9,6 @@ #ifndef LLVM_DEBUGINFO_PDB_NATIVE_NATIVETYPEVTSHAPE_H #define LLVM_DEBUGINFO_PDB_NATIVE_NATIVETYPEVTSHAPE_H -#include "llvm/ADT/Optional.h" #include "llvm/DebugInfo/CodeView/CodeView.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/PDB/Native/NativeRawSymbol.h" diff --git a/llvm/include/llvm/DebugInfo/PDB/Native/PDBFileBuilder.h b/llvm/include/llvm/DebugInfo/PDB/Native/PDBFileBuilder.h index 3c414e7a9005..004d005280d4 100644 --- a/llvm/include/llvm/DebugInfo/PDB/Native/PDBFileBuilder.h +++ b/llvm/include/llvm/DebugInfo/PDB/Native/PDBFileBuilder.h @@ -10,7 +10,6 @@ #define LLVM_DEBUGINFO_PDB_NATIVE_PDBFILEBUILDER_H #include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/DebugInfo/PDB/Native/NamedStreamMap.h" #include "llvm/DebugInfo/PDB/Native/PDBFile.h" diff --git a/llvm/include/llvm/Demangle/Demangle.h b/llvm/include/llvm/Demangle/Demangle.h index c396a1dc5dd3..3150e049320b 100644 --- a/llvm/include/llvm/Demangle/Demangle.h +++ b/llvm/include/llvm/Demangle/Demangle.h @@ -31,7 +31,6 @@ enum : int { char *itaniumDemangle(const char *mangled_name, char *buf, size_t *n, int *status); - enum MSDemangleFlags { MSDF_None = 0, MSDF_DumpBackrefs = 1 << 0, @@ -39,6 +38,7 @@ enum MSDemangleFlags { MSDF_NoCallingConvention = 1 << 2, MSDF_NoReturnType = 1 << 3, MSDF_NoMemberType = 1 << 4, + MSDF_NoVariableType = 1 << 5, }; /// Demangles the Microsoft symbol pointed at by mangled_name and returns it. @@ -53,13 +53,16 @@ enum MSDemangleFlags { /// receives the size of the demangled string on output if n_buf is not nullptr. /// status receives one of the demangle_ enum entries above if it's not nullptr. /// Flags controls various details of the demangled representation. -char *microsoftDemangle(const char *mangled_name, size_t *n_read, - char *buf, size_t *n_buf, - int *status, MSDemangleFlags Flags = MSDF_None); +char *microsoftDemangle(const char *mangled_name, size_t *n_read, char *buf, + size_t *n_buf, int *status, + MSDemangleFlags Flags = MSDF_None); // Demangles a Rust v0 mangled symbol. The API follows that of __cxa_demangle. char *rustDemangle(const char *MangledName, char *Buf, size_t *N, int *Status); +// Demangles a D mangled symbol. +char *dlangDemangle(const char *MangledName); + /// Attempt to demangle a string using different demangling schemes. /// The function uses heuristics to determine which demangling scheme to use. /// \param MangledName - reference to string to demangle. @@ -67,6 +70,8 @@ char *rustDemangle(const char *MangledName, char *Buf, size_t *N, int *Status); /// demangling occurred. std::string demangle(const std::string &MangledName); +bool nonMicrosoftDemangle(const char *MangledName, std::string &Result); + /// "Partial" demangler. This supports demangling a string into an AST /// (typically an intermediate stage in itaniumDemangle) and querying certain /// properties or partially printing the demangled name. @@ -118,6 +123,7 @@ struct ItaniumPartialDemangler { bool isSpecialName() const; ~ItaniumPartialDemangler(); + private: void *RootNode; void *Context; diff --git a/llvm/include/llvm/Demangle/ItaniumDemangle.h b/llvm/include/llvm/Demangle/ItaniumDemangle.h index 9163b713d118..86f5c992b63d 100644 --- a/llvm/include/llvm/Demangle/ItaniumDemangle.h +++ b/llvm/include/llvm/Demangle/ItaniumDemangle.h @@ -57,6 +57,7 @@ X(LocalName) \ X(VectorType) \ X(PixelVectorType) \ + X(BinaryFPType) \ X(SyntheticTemplateParamName) \ X(TypeTemplateParamDecl) \ X(NonTypeTemplateParamDecl) \ @@ -109,6 +110,126 @@ DEMANGLE_NAMESPACE_BEGIN +template class PODSmallVector { + static_assert(std::is_pod::value, + "T is required to be a plain old data type"); + + T *First = nullptr; + T *Last = nullptr; + T *Cap = nullptr; + T Inline[N] = {0}; + + bool isInline() const { return First == Inline; } + + void clearInline() { + First = Inline; + Last = Inline; + Cap = Inline + N; + } + + void reserve(size_t NewCap) { + size_t S = size(); + if (isInline()) { + auto *Tmp = static_cast(std::malloc(NewCap * sizeof(T))); + if (Tmp == nullptr) + std::terminate(); + std::copy(First, Last, Tmp); + First = Tmp; + } else { + First = static_cast(std::realloc(First, NewCap * sizeof(T))); + if (First == nullptr) + std::terminate(); + } + Last = First + S; + Cap = First + NewCap; + } + +public: + PODSmallVector() : First(Inline), Last(First), Cap(Inline + N) {} + + PODSmallVector(const PODSmallVector &) = delete; + PODSmallVector &operator=(const PODSmallVector &) = delete; + + PODSmallVector(PODSmallVector &&Other) : PODSmallVector() { + if (Other.isInline()) { + std::copy(Other.begin(), Other.end(), First); + Last = First + Other.size(); + Other.clear(); + return; + } + + First = Other.First; + Last = Other.Last; + Cap = Other.Cap; + Other.clearInline(); + } + + PODSmallVector &operator=(PODSmallVector &&Other) { + if (Other.isInline()) { + if (!isInline()) { + std::free(First); + clearInline(); + } + std::copy(Other.begin(), Other.end(), First); + Last = First + Other.size(); + Other.clear(); + return *this; + } + + if (isInline()) { + First = Other.First; + Last = Other.Last; + Cap = Other.Cap; + Other.clearInline(); + return *this; + } + + std::swap(First, Other.First); + std::swap(Last, Other.Last); + std::swap(Cap, Other.Cap); + Other.clear(); + return *this; + } + + // NOLINTNEXTLINE(readability-identifier-naming) + void push_back(const T &Elem) { + if (Last == Cap) + reserve(size() * 2); + *Last++ = Elem; + } + + // NOLINTNEXTLINE(readability-identifier-naming) + void pop_back() { + assert(Last != First && "Popping empty vector!"); + --Last; + } + + void dropBack(size_t Index) { + assert(Index <= size() && "dropBack() can't expand!"); + Last = First + Index; + } + + T *begin() { return First; } + T *end() { return Last; } + + bool empty() const { return First == Last; } + size_t size() const { return static_cast(Last - First); } + T &back() { + assert(Last != First && "Calling back() on empty vector!"); + return *(Last - 1); + } + T &operator[](size_t Index) { + assert(Index < size() && "Invalid access!"); + return *(begin() + Index); + } + void clear() { Last = First; } + + ~PODSmallVector() { + if (!isInline()) + std::free(First); + } +}; + // Base class of all AST nodes. The AST is built by the parser, then is // traversed by the printLeft/Right functions to produce a demangled string. class Node { @@ -155,50 +276,48 @@ public: // would construct an equivalent node. //template void match(Fn F) const; - bool hasRHSComponent(OutputStream &S) const { + bool hasRHSComponent(OutputBuffer &OB) const { if (RHSComponentCache != Cache::Unknown) return RHSComponentCache == Cache::Yes; - return hasRHSComponentSlow(S); + return hasRHSComponentSlow(OB); } - bool hasArray(OutputStream &S) const { + bool hasArray(OutputBuffer &OB) const { if (ArrayCache != Cache::Unknown) return ArrayCache == Cache::Yes; - return hasArraySlow(S); + return hasArraySlow(OB); } - bool hasFunction(OutputStream &S) const { + bool hasFunction(OutputBuffer &OB) const { if (FunctionCache != Cache::Unknown) return FunctionCache == Cache::Yes; - return hasFunctionSlow(S); + return hasFunctionSlow(OB); } Kind getKind() const { return K; } - virtual bool hasRHSComponentSlow(OutputStream &) const { return false; } - virtual bool hasArraySlow(OutputStream &) const { return false; } - virtual bool hasFunctionSlow(OutputStream &) const { return false; } + virtual bool hasRHSComponentSlow(OutputBuffer &) const { return false; } + virtual bool hasArraySlow(OutputBuffer &) const { return false; } + virtual bool hasFunctionSlow(OutputBuffer &) const { return false; } // Dig through "glue" nodes like ParameterPack and ForwardTemplateReference to // get at a node that actually represents some concrete syntax. - virtual const Node *getSyntaxNode(OutputStream &) const { - return this; - } + virtual const Node *getSyntaxNode(OutputBuffer &) const { return this; } - void print(OutputStream &S) const { - printLeft(S); + void print(OutputBuffer &OB) const { + printLeft(OB); if (RHSComponentCache != Cache::No) - printRight(S); + printRight(OB); } - // Print the "left" side of this Node into OutputStream. - virtual void printLeft(OutputStream &) const = 0; + // Print the "left" side of this Node into OutputBuffer. + virtual void printLeft(OutputBuffer &) const = 0; // Print the "right". This distinction is necessary to represent C++ types // that appear on the RHS of their subtype, such as arrays or functions. // Since most types don't have such a component, provide a default // implementation. - virtual void printRight(OutputStream &) const {} + virtual void printRight(OutputBuffer &) const {} virtual StringView getBaseName() const { return StringView(); } @@ -227,19 +346,19 @@ public: Node *operator[](size_t Idx) const { return Elements[Idx]; } - void printWithComma(OutputStream &S) const { + void printWithComma(OutputBuffer &OB) const { bool FirstElement = true; for (size_t Idx = 0; Idx != NumElements; ++Idx) { - size_t BeforeComma = S.getCurrentPosition(); + size_t BeforeComma = OB.getCurrentPosition(); if (!FirstElement) - S += ", "; - size_t AfterComma = S.getCurrentPosition(); - Elements[Idx]->print(S); + OB += ", "; + size_t AfterComma = OB.getCurrentPosition(); + Elements[Idx]->print(OB); // Elements[Idx] is an empty parameter pack expansion, we should erase the // comma we just printed. - if (AfterComma == S.getCurrentPosition()) { - S.setCurrentPosition(BeforeComma); + if (AfterComma == OB.getCurrentPosition()) { + OB.setCurrentPosition(BeforeComma); continue; } @@ -254,9 +373,7 @@ struct NodeArrayNode : Node { template void match(Fn F) const { F(Array); } - void printLeft(OutputStream &S) const override { - Array.printWithComma(S); - } + void printLeft(OutputBuffer &OB) const override { Array.printWithComma(OB); } }; class DotSuffix final : public Node { @@ -269,11 +386,11 @@ public: template void match(Fn F) const { F(Prefix, Suffix); } - void printLeft(OutputStream &s) const override { - Prefix->print(s); - s += " ("; - s += Suffix; - s += ")"; + void printLeft(OutputBuffer &OB) const override { + Prefix->print(OB); + OB += " ("; + OB += Suffix; + OB += ")"; } }; @@ -288,12 +405,12 @@ public: template void match(Fn F) const { F(Ty, Ext, TA); } - void printLeft(OutputStream &S) const override { - Ty->print(S); - S += " "; - S += Ext; + void printLeft(OutputBuffer &OB) const override { + Ty->print(OB); + OB += " "; + OB += Ext; if (TA != nullptr) - TA->print(S); + TA->print(OB); } }; @@ -319,13 +436,13 @@ protected: const Qualifiers Quals; const Node *Child; - void printQuals(OutputStream &S) const { + void printQuals(OutputBuffer &OB) const { if (Quals & QualConst) - S += " const"; + OB += " const"; if (Quals & QualVolatile) - S += " volatile"; + OB += " volatile"; if (Quals & QualRestrict) - S += " restrict"; + OB += " restrict"; } public: @@ -336,22 +453,22 @@ public: template void match(Fn F) const { F(Child, Quals); } - bool hasRHSComponentSlow(OutputStream &S) const override { - return Child->hasRHSComponent(S); + bool hasRHSComponentSlow(OutputBuffer &OB) const override { + return Child->hasRHSComponent(OB); } - bool hasArraySlow(OutputStream &S) const override { - return Child->hasArray(S); + bool hasArraySlow(OutputBuffer &OB) const override { + return Child->hasArray(OB); } - bool hasFunctionSlow(OutputStream &S) const override { - return Child->hasFunction(S); + bool hasFunctionSlow(OutputBuffer &OB) const override { + return Child->hasFunction(OB); } - void printLeft(OutputStream &S) const override { - Child->printLeft(S); - printQuals(S); + void printLeft(OutputBuffer &OB) const override { + Child->printLeft(OB); + printQuals(OB); } - void printRight(OutputStream &S) const override { Child->printRight(S); } + void printRight(OutputBuffer &OB) const override { Child->printRight(OB); } }; class ConversionOperatorType final : public Node { @@ -363,9 +480,9 @@ public: template void match(Fn F) const { F(Ty); } - void printLeft(OutputStream &S) const override { - S += "operator "; - Ty->print(S); + void printLeft(OutputBuffer &OB) const override { + OB += "operator "; + Ty->print(OB); } }; @@ -379,9 +496,9 @@ public: template void match(Fn F) const { F(Ty, Postfix); } - void printLeft(OutputStream &s) const override { - Ty->printLeft(s); - s += Postfix; + void printLeft(OutputBuffer &OB) const override { + Ty->printLeft(OB); + OB += Postfix; } }; @@ -396,7 +513,7 @@ public: StringView getName() const { return Name; } StringView getBaseName() const override { return Name; } - void printLeft(OutputStream &s) const override { s += Name; } + void printLeft(OutputBuffer &OB) const override { OB += Name; } }; class ElaboratedTypeSpefType : public Node { @@ -408,10 +525,10 @@ public: template void match(Fn F) const { F(Kind, Child); } - void printLeft(OutputStream &S) const override { - S += Kind; - S += ' '; - Child->print(S); + void printLeft(OutputBuffer &OB) const override { + OB += Kind; + OB += ' '; + Child->print(OB); } }; @@ -426,11 +543,11 @@ struct AbiTagAttr : Node { template void match(Fn F) const { F(Base, Tag); } - void printLeft(OutputStream &S) const override { - Base->printLeft(S); - S += "[abi:"; - S += Tag; - S += "]"; + void printLeft(OutputBuffer &OB) const override { + Base->printLeft(OB); + OB += "[abi:"; + OB += Tag; + OB += "]"; } }; @@ -442,10 +559,10 @@ public: template void match(Fn F) const { F(Conditions); } - void printLeft(OutputStream &S) const override { - S += " [enable_if:"; - Conditions.printWithComma(S); - S += ']'; + void printLeft(OutputBuffer &OB) const override { + OB += " [enable_if:"; + Conditions.printWithComma(OB); + OB += ']'; } }; @@ -466,11 +583,11 @@ public: static_cast(Ty)->getName() == "objc_object"; } - void printLeft(OutputStream &S) const override { - Ty->print(S); - S += "<"; - S += Protocol; - S += ">"; + void printLeft(OutputBuffer &OB) const override { + Ty->print(OB); + OB += "<"; + OB += Protocol; + OB += ">"; } }; @@ -484,34 +601,34 @@ public: template void match(Fn F) const { F(Pointee); } - bool hasRHSComponentSlow(OutputStream &S) const override { - return Pointee->hasRHSComponent(S); + bool hasRHSComponentSlow(OutputBuffer &OB) const override { + return Pointee->hasRHSComponent(OB); } - void printLeft(OutputStream &s) const override { + void printLeft(OutputBuffer &OB) const override { // We rewrite objc_object* into id. if (Pointee->getKind() != KObjCProtoName || !static_cast(Pointee)->isObjCObject()) { - Pointee->printLeft(s); - if (Pointee->hasArray(s)) - s += " "; - if (Pointee->hasArray(s) || Pointee->hasFunction(s)) - s += "("; - s += "*"; + Pointee->printLeft(OB); + if (Pointee->hasArray(OB)) + OB += " "; + if (Pointee->hasArray(OB) || Pointee->hasFunction(OB)) + OB += "("; + OB += "*"; } else { const auto *objcProto = static_cast(Pointee); - s += "id<"; - s += objcProto->Protocol; - s += ">"; + OB += "id<"; + OB += objcProto->Protocol; + OB += ">"; } } - void printRight(OutputStream &s) const override { + void printRight(OutputBuffer &OB) const override { if (Pointee->getKind() != KObjCProtoName || !static_cast(Pointee)->isObjCObject()) { - if (Pointee->hasArray(s) || Pointee->hasFunction(s)) - s += ")"; - Pointee->printRight(s); + if (Pointee->hasArray(OB) || Pointee->hasFunction(OB)) + OB += ")"; + Pointee->printRight(OB); } } }; @@ -531,15 +648,30 @@ class ReferenceType : public Node { // Dig through any refs to refs, collapsing the ReferenceTypes as we go. The // rule here is rvalue ref to rvalue ref collapses to a rvalue ref, and any // other combination collapses to a lvalue ref. - std::pair collapse(OutputStream &S) const { + // + // A combination of a TemplateForwardReference and a back-ref Substitution + // from an ill-formed string may have created a cycle; use cycle detection to + // avoid looping forever. + std::pair collapse(OutputBuffer &OB) const { auto SoFar = std::make_pair(RK, Pointee); + // Track the chain of nodes for the Floyd's 'tortoise and hare' + // cycle-detection algorithm, since getSyntaxNode(S) is impure + PODSmallVector Prev; for (;;) { - const Node *SN = SoFar.second->getSyntaxNode(S); + const Node *SN = SoFar.second->getSyntaxNode(OB); if (SN->getKind() != KReferenceType) break; auto *RT = static_cast(SN); SoFar.second = RT->Pointee; SoFar.first = std::min(SoFar.first, RT->RK); + + // The middle of Prev is the 'slow' pointer moving at half speed + Prev.push_back(SoFar.second); + if (Prev.size() > 1 && SoFar.second == Prev[(Prev.size() - 1) / 2]) { + // Cycle detected + SoFar.second = nullptr; + break; + } } return SoFar; } @@ -551,31 +683,35 @@ public: template void match(Fn F) const { F(Pointee, RK); } - bool hasRHSComponentSlow(OutputStream &S) const override { - return Pointee->hasRHSComponent(S); + bool hasRHSComponentSlow(OutputBuffer &OB) const override { + return Pointee->hasRHSComponent(OB); } - void printLeft(OutputStream &s) const override { + void printLeft(OutputBuffer &OB) const override { if (Printing) return; SwapAndRestore SavePrinting(Printing, true); - std::pair Collapsed = collapse(s); - Collapsed.second->printLeft(s); - if (Collapsed.second->hasArray(s)) - s += " "; - if (Collapsed.second->hasArray(s) || Collapsed.second->hasFunction(s)) - s += "("; + std::pair Collapsed = collapse(OB); + if (!Collapsed.second) + return; + Collapsed.second->printLeft(OB); + if (Collapsed.second->hasArray(OB)) + OB += " "; + if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB)) + OB += "("; - s += (Collapsed.first == ReferenceKind::LValue ? "&" : "&&"); + OB += (Collapsed.first == ReferenceKind::LValue ? "&" : "&&"); } - void printRight(OutputStream &s) const override { + void printRight(OutputBuffer &OB) const override { if (Printing) return; SwapAndRestore SavePrinting(Printing, true); - std::pair Collapsed = collapse(s); - if (Collapsed.second->hasArray(s) || Collapsed.second->hasFunction(s)) - s += ")"; - Collapsed.second->printRight(s); + std::pair Collapsed = collapse(OB); + if (!Collapsed.second) + return; + if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB)) + OB += ")"; + Collapsed.second->printRight(OB); } }; @@ -590,24 +726,24 @@ public: template void match(Fn F) const { F(ClassType, MemberType); } - bool hasRHSComponentSlow(OutputStream &S) const override { - return MemberType->hasRHSComponent(S); + bool hasRHSComponentSlow(OutputBuffer &OB) const override { + return MemberType->hasRHSComponent(OB); } - void printLeft(OutputStream &s) const override { - MemberType->printLeft(s); - if (MemberType->hasArray(s) || MemberType->hasFunction(s)) - s += "("; + void printLeft(OutputBuffer &OB) const override { + MemberType->printLeft(OB); + if (MemberType->hasArray(OB) || MemberType->hasFunction(OB)) + OB += "("; else - s += " "; - ClassType->print(s); - s += "::*"; + OB += " "; + ClassType->print(OB); + OB += "::*"; } - void printRight(OutputStream &s) const override { - if (MemberType->hasArray(s) || MemberType->hasFunction(s)) - s += ")"; - MemberType->printRight(s); + void printRight(OutputBuffer &OB) const override { + if (MemberType->hasArray(OB) || MemberType->hasFunction(OB)) + OB += ")"; + MemberType->printRight(OB); } }; @@ -624,19 +760,19 @@ public: template void match(Fn F) const { F(Base, Dimension); } - bool hasRHSComponentSlow(OutputStream &) const override { return true; } - bool hasArraySlow(OutputStream &) const override { return true; } + bool hasRHSComponentSlow(OutputBuffer &) const override { return true; } + bool hasArraySlow(OutputBuffer &) const override { return true; } - void printLeft(OutputStream &S) const override { Base->printLeft(S); } + void printLeft(OutputBuffer &OB) const override { Base->printLeft(OB); } - void printRight(OutputStream &S) const override { - if (S.back() != ']') - S += " "; - S += "["; + void printRight(OutputBuffer &OB) const override { + if (OB.back() != ']') + OB += " "; + OB += "["; if (Dimension) - Dimension->print(S); - S += "]"; - Base->printRight(S); + Dimension->print(OB); + OB += "]"; + Base->printRight(OB); } }; @@ -660,8 +796,8 @@ public: F(Ret, Params, CVQuals, RefQual, ExceptionSpec); } - bool hasRHSComponentSlow(OutputStream &) const override { return true; } - bool hasFunctionSlow(OutputStream &) const override { return true; } + bool hasRHSComponentSlow(OutputBuffer &) const override { return true; } + bool hasFunctionSlow(OutputBuffer &) const override { return true; } // Handle C++'s ... quirky decl grammar by using the left & right // distinction. Consider: @@ -670,32 +806,32 @@ public: // that takes a char and returns an int. If we're trying to print f, start // by printing out the return types's left, then print our parameters, then // finally print right of the return type. - void printLeft(OutputStream &S) const override { - Ret->printLeft(S); - S += " "; + void printLeft(OutputBuffer &OB) const override { + Ret->printLeft(OB); + OB += " "; } - void printRight(OutputStream &S) const override { - S += "("; - Params.printWithComma(S); - S += ")"; - Ret->printRight(S); + void printRight(OutputBuffer &OB) const override { + OB += "("; + Params.printWithComma(OB); + OB += ")"; + Ret->printRight(OB); if (CVQuals & QualConst) - S += " const"; + OB += " const"; if (CVQuals & QualVolatile) - S += " volatile"; + OB += " volatile"; if (CVQuals & QualRestrict) - S += " restrict"; + OB += " restrict"; if (RefQual == FrefQualLValue) - S += " &"; + OB += " &"; else if (RefQual == FrefQualRValue) - S += " &&"; + OB += " &&"; if (ExceptionSpec != nullptr) { - S += ' '; - ExceptionSpec->print(S); + OB += ' '; + ExceptionSpec->print(OB); } } }; @@ -707,10 +843,10 @@ public: template void match(Fn F) const { F(E); } - void printLeft(OutputStream &S) const override { - S += "noexcept("; - E->print(S); - S += ")"; + void printLeft(OutputBuffer &OB) const override { + OB += "noexcept("; + E->print(OB); + OB += ")"; } }; @@ -722,10 +858,10 @@ public: template void match(Fn F) const { F(Types); } - void printLeft(OutputStream &S) const override { - S += "throw("; - Types.printWithComma(S); - S += ')'; + void printLeft(OutputBuffer &OB) const override { + OB += "throw("; + Types.printWithComma(OB); + OB += ')'; } }; @@ -756,41 +892,41 @@ public: NodeArray getParams() const { return Params; } const Node *getReturnType() const { return Ret; } - bool hasRHSComponentSlow(OutputStream &) const override { return true; } - bool hasFunctionSlow(OutputStream &) const override { return true; } + bool hasRHSComponentSlow(OutputBuffer &) const override { return true; } + bool hasFunctionSlow(OutputBuffer &) const override { return true; } const Node *getName() const { return Name; } - void printLeft(OutputStream &S) const override { + void printLeft(OutputBuffer &OB) const override { if (Ret) { - Ret->printLeft(S); - if (!Ret->hasRHSComponent(S)) - S += " "; + Ret->printLeft(OB); + if (!Ret->hasRHSComponent(OB)) + OB += " "; } - Name->print(S); + Name->print(OB); } - void printRight(OutputStream &S) const override { - S += "("; - Params.printWithComma(S); - S += ")"; + void printRight(OutputBuffer &OB) const override { + OB += "("; + Params.printWithComma(OB); + OB += ")"; if (Ret) - Ret->printRight(S); + Ret->printRight(OB); if (CVQuals & QualConst) - S += " const"; + OB += " const"; if (CVQuals & QualVolatile) - S += " volatile"; + OB += " volatile"; if (CVQuals & QualRestrict) - S += " restrict"; + OB += " restrict"; if (RefQual == FrefQualLValue) - S += " &"; + OB += " &"; else if (RefQual == FrefQualRValue) - S += " &&"; + OB += " &&"; if (Attrs != nullptr) - Attrs->print(S); + Attrs->print(OB); } }; @@ -803,9 +939,9 @@ public: template void match(Fn F) const { F(OpName); } - void printLeft(OutputStream &S) const override { - S += "operator\"\" "; - OpName->print(S); + void printLeft(OutputBuffer &OB) const override { + OB += "operator\"\" "; + OpName->print(OB); } }; @@ -819,9 +955,9 @@ public: template void match(Fn F) const { F(Special, Child); } - void printLeft(OutputStream &S) const override { - S += Special; - Child->print(S); + void printLeft(OutputBuffer &OB) const override { + OB += Special; + Child->print(OB); } }; @@ -836,11 +972,11 @@ public: template void match(Fn F) const { F(FirstType, SecondType); } - void printLeft(OutputStream &S) const override { - S += "construction vtable for "; - FirstType->print(S); - S += "-in-"; - SecondType->print(S); + void printLeft(OutputBuffer &OB) const override { + OB += "construction vtable for "; + FirstType->print(OB); + OB += "-in-"; + SecondType->print(OB); } }; @@ -855,10 +991,10 @@ struct NestedName : Node { StringView getBaseName() const override { return Name->getBaseName(); } - void printLeft(OutputStream &S) const override { - Qual->print(S); - S += "::"; - Name->print(S); + void printLeft(OutputBuffer &OB) const override { + Qual->print(OB); + OB += "::"; + Name->print(OB); } }; @@ -871,10 +1007,10 @@ struct LocalName : Node { template void match(Fn F) const { F(Encoding, Entity); } - void printLeft(OutputStream &S) const override { - Encoding->print(S); - S += "::"; - Entity->print(S); + void printLeft(OutputBuffer &OB) const override { + Encoding->print(OB); + OB += "::"; + Entity->print(OB); } }; @@ -891,10 +1027,10 @@ public: StringView getBaseName() const override { return Name->getBaseName(); } - void printLeft(OutputStream &S) const override { - Qualifier->print(S); - S += "::"; - Name->print(S); + void printLeft(OutputBuffer &OB) const override { + Qualifier->print(OB); + OB += "::"; + Name->print(OB); } }; @@ -909,12 +1045,12 @@ public: template void match(Fn F) const { F(BaseType, Dimension); } - void printLeft(OutputStream &S) const override { - BaseType->print(S); - S += " vector["; + void printLeft(OutputBuffer &OB) const override { + BaseType->print(OB); + OB += " vector["; if (Dimension) - Dimension->print(S); - S += "]"; + Dimension->print(OB); + OB += "]"; } }; @@ -927,11 +1063,26 @@ public: template void match(Fn F) const { F(Dimension); } - void printLeft(OutputStream &S) const override { + void printLeft(OutputBuffer &OB) const override { // FIXME: This should demangle as "vector pixel". - S += "pixel vector["; - Dimension->print(S); - S += "]"; + OB += "pixel vector["; + Dimension->print(OB); + OB += "]"; + } +}; + +class BinaryFPType final : public Node { + const Node *Dimension; + +public: + BinaryFPType(const Node *Dimension_) + : Node(KBinaryFPType), Dimension(Dimension_) {} + + template void match(Fn F) const { F(Dimension); } + + void printLeft(OutputBuffer &OB) const override { + OB += "_Float"; + Dimension->print(OB); } }; @@ -953,20 +1104,20 @@ public: template void match(Fn F) const { F(Kind, Index); } - void printLeft(OutputStream &S) const override { + void printLeft(OutputBuffer &OB) const override { switch (Kind) { case TemplateParamKind::Type: - S += "$T"; + OB += "$T"; break; case TemplateParamKind::NonType: - S += "$N"; + OB += "$N"; break; case TemplateParamKind::Template: - S += "$TT"; + OB += "$TT"; break; } if (Index > 0) - S << Index - 1; + OB << Index - 1; } }; @@ -980,13 +1131,9 @@ public: template void match(Fn F) const { F(Name); } - void printLeft(OutputStream &S) const override { - S += "typename "; - } + void printLeft(OutputBuffer &OB) const override { OB += "typename "; } - void printRight(OutputStream &S) const override { - Name->print(S); - } + void printRight(OutputBuffer &OB) const override { Name->print(OB); } }; /// A non-type template parameter declaration, 'int N'. @@ -1000,15 +1147,15 @@ public: template void match(Fn F) const { F(Name, Type); } - void printLeft(OutputStream &S) const override { - Type->printLeft(S); - if (!Type->hasRHSComponent(S)) - S += " "; + void printLeft(OutputBuffer &OB) const override { + Type->printLeft(OB); + if (!Type->hasRHSComponent(OB)) + OB += " "; } - void printRight(OutputStream &S) const override { - Name->print(S); - Type->printRight(S); + void printRight(OutputBuffer &OB) const override { + Name->print(OB); + Type->printRight(OB); } }; @@ -1025,15 +1172,13 @@ public: template void match(Fn F) const { F(Name, Params); } - void printLeft(OutputStream &S) const override { - S += "template<"; - Params.printWithComma(S); - S += "> typename "; + void printLeft(OutputBuffer &OB) const override { + OB += "template<"; + Params.printWithComma(OB); + OB += "> typename "; } - void printRight(OutputStream &S) const override { - Name->print(S); - } + void printRight(OutputBuffer &OB) const override { Name->print(OB); } }; /// A template parameter pack declaration, 'typename ...T'. @@ -1046,14 +1191,12 @@ public: template void match(Fn F) const { F(Param); } - void printLeft(OutputStream &S) const override { - Param->printLeft(S); - S += "..."; + void printLeft(OutputBuffer &OB) const override { + Param->printLeft(OB); + OB += "..."; } - void printRight(OutputStream &S) const override { - Param->printRight(S); - } + void printRight(OutputBuffer &OB) const override { Param->printRight(OB); } }; /// An unexpanded parameter pack (either in the expression or type context). If @@ -1067,11 +1210,11 @@ public: class ParameterPack final : public Node { NodeArray Data; - // Setup OutputStream for a pack expansion unless we're already expanding one. - void initializePackExpansion(OutputStream &S) const { - if (S.CurrentPackMax == std::numeric_limits::max()) { - S.CurrentPackMax = static_cast(Data.size()); - S.CurrentPackIndex = 0; + // Setup OutputBuffer for a pack expansion unless we're already expanding one. + void initializePackExpansion(OutputBuffer &OB) const { + if (OB.CurrentPackMax == std::numeric_limits::max()) { + OB.CurrentPackMax = static_cast(Data.size()); + OB.CurrentPackIndex = 0; } } @@ -1094,38 +1237,38 @@ public: template void match(Fn F) const { F(Data); } - bool hasRHSComponentSlow(OutputStream &S) const override { - initializePackExpansion(S); - size_t Idx = S.CurrentPackIndex; - return Idx < Data.size() && Data[Idx]->hasRHSComponent(S); + bool hasRHSComponentSlow(OutputBuffer &OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; + return Idx < Data.size() && Data[Idx]->hasRHSComponent(OB); } - bool hasArraySlow(OutputStream &S) const override { - initializePackExpansion(S); - size_t Idx = S.CurrentPackIndex; - return Idx < Data.size() && Data[Idx]->hasArray(S); + bool hasArraySlow(OutputBuffer &OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; + return Idx < Data.size() && Data[Idx]->hasArray(OB); } - bool hasFunctionSlow(OutputStream &S) const override { - initializePackExpansion(S); - size_t Idx = S.CurrentPackIndex; - return Idx < Data.size() && Data[Idx]->hasFunction(S); + bool hasFunctionSlow(OutputBuffer &OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; + return Idx < Data.size() && Data[Idx]->hasFunction(OB); } - const Node *getSyntaxNode(OutputStream &S) const override { - initializePackExpansion(S); - size_t Idx = S.CurrentPackIndex; - return Idx < Data.size() ? Data[Idx]->getSyntaxNode(S) : this; + const Node *getSyntaxNode(OutputBuffer &OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; + return Idx < Data.size() ? Data[Idx]->getSyntaxNode(OB) : this; } - void printLeft(OutputStream &S) const override { - initializePackExpansion(S); - size_t Idx = S.CurrentPackIndex; + void printLeft(OutputBuffer &OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; if (Idx < Data.size()) - Data[Idx]->printLeft(S); + Data[Idx]->printLeft(OB); } - void printRight(OutputStream &S) const override { - initializePackExpansion(S); - size_t Idx = S.CurrentPackIndex; + void printRight(OutputBuffer &OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; if (Idx < Data.size()) - Data[Idx]->printRight(S); + Data[Idx]->printRight(OB); } }; @@ -1144,8 +1287,8 @@ public: NodeArray getElements() const { return Elements; } - void printLeft(OutputStream &S) const override { - Elements.printWithComma(S); + void printLeft(OutputBuffer &OB) const override { + Elements.printWithComma(OB); } }; @@ -1162,35 +1305,35 @@ public: const Node *getChild() const { return Child; } - void printLeft(OutputStream &S) const override { + void printLeft(OutputBuffer &OB) const override { constexpr unsigned Max = std::numeric_limits::max(); - SwapAndRestore SavePackIdx(S.CurrentPackIndex, Max); - SwapAndRestore SavePackMax(S.CurrentPackMax, Max); - size_t StreamPos = S.getCurrentPosition(); + SwapAndRestore SavePackIdx(OB.CurrentPackIndex, Max); + SwapAndRestore SavePackMax(OB.CurrentPackMax, Max); + size_t StreamPos = OB.getCurrentPosition(); // Print the first element in the pack. If Child contains a ParameterPack, // it will set up S.CurrentPackMax and print the first element. - Child->print(S); + Child->print(OB); // No ParameterPack was found in Child. This can occur if we've found a pack // expansion on a . - if (S.CurrentPackMax == Max) { - S += "..."; + if (OB.CurrentPackMax == Max) { + OB += "..."; return; } // We found a ParameterPack, but it has no elements. Erase whatever we may // of printed. - if (S.CurrentPackMax == 0) { - S.setCurrentPosition(StreamPos); + if (OB.CurrentPackMax == 0) { + OB.setCurrentPosition(StreamPos); return; } // Else, iterate through the rest of the elements in the pack. - for (unsigned I = 1, E = S.CurrentPackMax; I < E; ++I) { - S += ", "; - S.CurrentPackIndex = I; - Child->print(S); + for (unsigned I = 1, E = OB.CurrentPackMax; I < E; ++I) { + OB += ", "; + OB.CurrentPackIndex = I; + Child->print(OB); } } }; @@ -1205,12 +1348,12 @@ public: NodeArray getParams() { return Params; } - void printLeft(OutputStream &S) const override { - S += "<"; - Params.printWithComma(S); - if (S.back() == '>') - S += " "; - S += ">"; + void printLeft(OutputBuffer &OB) const override { + OB += "<"; + Params.printWithComma(OB); + if (OB.back() == '>') + OB += " "; + OB += ">"; } }; @@ -1252,42 +1395,42 @@ struct ForwardTemplateReference : Node { // special handling. template void match(Fn F) const = delete; - bool hasRHSComponentSlow(OutputStream &S) const override { + bool hasRHSComponentSlow(OutputBuffer &OB) const override { if (Printing) return false; SwapAndRestore SavePrinting(Printing, true); - return Ref->hasRHSComponent(S); + return Ref->hasRHSComponent(OB); } - bool hasArraySlow(OutputStream &S) const override { + bool hasArraySlow(OutputBuffer &OB) const override { if (Printing) return false; SwapAndRestore SavePrinting(Printing, true); - return Ref->hasArray(S); + return Ref->hasArray(OB); } - bool hasFunctionSlow(OutputStream &S) const override { + bool hasFunctionSlow(OutputBuffer &OB) const override { if (Printing) return false; SwapAndRestore SavePrinting(Printing, true); - return Ref->hasFunction(S); + return Ref->hasFunction(OB); } - const Node *getSyntaxNode(OutputStream &S) const override { + const Node *getSyntaxNode(OutputBuffer &OB) const override { if (Printing) return this; SwapAndRestore SavePrinting(Printing, true); - return Ref->getSyntaxNode(S); + return Ref->getSyntaxNode(OB); } - void printLeft(OutputStream &S) const override { + void printLeft(OutputBuffer &OB) const override { if (Printing) return; SwapAndRestore SavePrinting(Printing, true); - Ref->printLeft(S); + Ref->printLeft(OB); } - void printRight(OutputStream &S) const override { + void printRight(OutputBuffer &OB) const override { if (Printing) return; SwapAndRestore SavePrinting(Printing, true); - Ref->printRight(S); + Ref->printRight(OB); } }; @@ -1303,9 +1446,9 @@ struct NameWithTemplateArgs : Node { StringView getBaseName() const override { return Name->getBaseName(); } - void printLeft(OutputStream &S) const override { - Name->print(S); - TemplateArgs->print(S); + void printLeft(OutputBuffer &OB) const override { + Name->print(OB); + TemplateArgs->print(OB); } }; @@ -1320,9 +1463,9 @@ public: StringView getBaseName() const override { return Child->getBaseName(); } - void printLeft(OutputStream &S) const override { - S += "::"; - Child->print(S); + void printLeft(OutputBuffer &OB) const override { + OB += "::"; + Child->print(OB); } }; @@ -1335,9 +1478,9 @@ struct StdQualifiedName : Node { StringView getBaseName() const override { return Child->getBaseName(); } - void printLeft(OutputStream &S) const override { - S += "std::"; - Child->print(S); + void printLeft(OutputBuffer &OB) const override { + OB += "std::"; + Child->print(OB); } }; @@ -1377,26 +1520,26 @@ public: DEMANGLE_UNREACHABLE; } - void printLeft(OutputStream &S) const override { + void printLeft(OutputBuffer &OB) const override { switch (SSK) { case SpecialSubKind::allocator: - S += "std::allocator"; + OB += "std::allocator"; break; case SpecialSubKind::basic_string: - S += "std::basic_string"; + OB += "std::basic_string"; break; case SpecialSubKind::string: - S += "std::basic_string, " - "std::allocator >"; + OB += "std::basic_string, " + "std::allocator >"; break; case SpecialSubKind::istream: - S += "std::basic_istream >"; + OB += "std::basic_istream >"; break; case SpecialSubKind::ostream: - S += "std::basic_ostream >"; + OB += "std::basic_ostream >"; break; case SpecialSubKind::iostream: - S += "std::basic_iostream >"; + OB += "std::basic_iostream >"; break; } } @@ -1429,25 +1572,25 @@ public: DEMANGLE_UNREACHABLE; } - void printLeft(OutputStream &S) const override { + void printLeft(OutputBuffer &OB) const override { switch (SSK) { case SpecialSubKind::allocator: - S += "std::allocator"; + OB += "std::allocator"; break; case SpecialSubKind::basic_string: - S += "std::basic_string"; + OB += "std::basic_string"; break; case SpecialSubKind::string: - S += "std::string"; + OB += "std::string"; break; case SpecialSubKind::istream: - S += "std::istream"; + OB += "std::istream"; break; case SpecialSubKind::ostream: - S += "std::ostream"; + OB += "std::ostream"; break; case SpecialSubKind::iostream: - S += "std::iostream"; + OB += "std::iostream"; break; } } @@ -1465,10 +1608,10 @@ public: template void match(Fn F) const { F(Basename, IsDtor, Variant); } - void printLeft(OutputStream &S) const override { + void printLeft(OutputBuffer &OB) const override { if (IsDtor) - S += "~"; - S += Basename->getBaseName(); + OB += "~"; + OB += Basename->getBaseName(); } }; @@ -1480,9 +1623,9 @@ public: template void match(Fn F) const { F(Base); } - void printLeft(OutputStream &S) const override { - S += "~"; - Base->printLeft(S); + void printLeft(OutputBuffer &OB) const override { + OB += "~"; + Base->printLeft(OB); } }; @@ -1494,10 +1637,10 @@ public: template void match(Fn F) const { F(Count); } - void printLeft(OutputStream &S) const override { - S += "'unnamed"; - S += Count; - S += "\'"; + void printLeft(OutputBuffer &OB) const override { + OB += "'unnamed"; + OB += Count; + OB += "\'"; } }; @@ -1516,22 +1659,22 @@ public: F(TemplateParams, Params, Count); } - void printDeclarator(OutputStream &S) const { + void printDeclarator(OutputBuffer &OB) const { if (!TemplateParams.empty()) { - S += "<"; - TemplateParams.printWithComma(S); - S += ">"; + OB += "<"; + TemplateParams.printWithComma(OB); + OB += ">"; } - S += "("; - Params.printWithComma(S); - S += ")"; + OB += "("; + Params.printWithComma(OB); + OB += ")"; } - void printLeft(OutputStream &S) const override { - S += "\'lambda"; - S += Count; - S += "\'"; - printDeclarator(S); + void printLeft(OutputBuffer &OB) const override { + OB += "\'lambda"; + OB += Count; + OB += "\'"; + printDeclarator(OB); } }; @@ -1543,10 +1686,10 @@ public: template void match(Fn F) const { F(Bindings); } - void printLeft(OutputStream &S) const override { - S += '['; - Bindings.printWithComma(S); - S += ']'; + void printLeft(OutputBuffer &OB) const override { + OB += '['; + Bindings.printWithComma(OB); + OB += ']'; } }; @@ -1564,22 +1707,22 @@ public: template void match(Fn F) const { F(LHS, InfixOperator, RHS); } - void printLeft(OutputStream &S) const override { + void printLeft(OutputBuffer &OB) const override { // might be a template argument expression, then we need to disambiguate // with parens. if (InfixOperator == ">") - S += "("; + OB += "("; - S += "("; - LHS->print(S); - S += ") "; - S += InfixOperator; - S += " ("; - RHS->print(S); - S += ")"; + OB += "("; + LHS->print(OB); + OB += ") "; + OB += InfixOperator; + OB += " ("; + RHS->print(OB); + OB += ")"; if (InfixOperator == ">") - S += ")"; + OB += ")"; } }; @@ -1593,12 +1736,12 @@ public: template void match(Fn F) const { F(Op1, Op2); } - void printLeft(OutputStream &S) const override { - S += "("; - Op1->print(S); - S += ")["; - Op2->print(S); - S += "]"; + void printLeft(OutputBuffer &OB) const override { + OB += "("; + Op1->print(OB); + OB += ")["; + Op2->print(OB); + OB += "]"; } }; @@ -1612,11 +1755,11 @@ public: template void match(Fn F) const { F(Child, Operator); } - void printLeft(OutputStream &S) const override { - S += "("; - Child->print(S); - S += ")"; - S += Operator; + void printLeft(OutputBuffer &OB) const override { + OB += "("; + Child->print(OB); + OB += ")"; + OB += Operator; } }; @@ -1631,14 +1774,14 @@ public: template void match(Fn F) const { F(Cond, Then, Else); } - void printLeft(OutputStream &S) const override { - S += "("; - Cond->print(S); - S += ") ? ("; - Then->print(S); - S += ") : ("; - Else->print(S); - S += ")"; + void printLeft(OutputBuffer &OB) const override { + OB += "("; + Cond->print(OB); + OB += ") ? ("; + Then->print(OB); + OB += ") : ("; + Else->print(OB); + OB += ")"; } }; @@ -1653,10 +1796,10 @@ public: template void match(Fn F) const { F(LHS, Kind, RHS); } - void printLeft(OutputStream &S) const override { - LHS->print(S); - S += Kind; - RHS->print(S); + void printLeft(OutputBuffer &OB) const override { + LHS->print(OB); + OB += Kind; + RHS->print(OB); } }; @@ -1677,20 +1820,20 @@ public: F(Type, SubExpr, Offset, UnionSelectors, OnePastTheEnd); } - void printLeft(OutputStream &S) const override { - SubExpr->print(S); - S += ".<"; - Type->print(S); - S += " at offset "; + void printLeft(OutputBuffer &OB) const override { + SubExpr->print(OB); + OB += ".<"; + Type->print(OB); + OB += " at offset "; if (Offset.empty()) { - S += "0"; + OB += "0"; } else if (Offset[0] == 'n') { - S += "-"; - S += Offset.dropFront(); + OB += "-"; + OB += Offset.dropFront(); } else { - S += Offset; + OB += Offset; } - S += ">"; + OB += ">"; } }; @@ -1706,10 +1849,10 @@ public: template void match(Fn F) const { F(Prefix, Infix, Postfix); } - void printLeft(OutputStream &S) const override { - S += Prefix; - Infix->print(S); - S += Postfix; + void printLeft(OutputBuffer &OB) const override { + OB += Prefix; + Infix->print(OB); + OB += Postfix; } }; @@ -1725,13 +1868,13 @@ public: template void match(Fn F) const { F(CastKind, To, From); } - void printLeft(OutputStream &S) const override { - S += CastKind; - S += "<"; - To->printLeft(S); - S += ">("; - From->printLeft(S); - S += ")"; + void printLeft(OutputBuffer &OB) const override { + OB += CastKind; + OB += "<"; + To->printLeft(OB); + OB += ">("; + From->printLeft(OB); + OB += ")"; } }; @@ -1744,11 +1887,11 @@ public: template void match(Fn F) const { F(Pack); } - void printLeft(OutputStream &S) const override { - S += "sizeof...("; + void printLeft(OutputBuffer &OB) const override { + OB += "sizeof...("; ParameterPackExpansion PPE(Pack); - PPE.printLeft(S); - S += ")"; + PPE.printLeft(OB); + OB += ")"; } }; @@ -1762,11 +1905,11 @@ public: template void match(Fn F) const { F(Callee, Args); } - void printLeft(OutputStream &S) const override { - Callee->print(S); - S += "("; - Args.printWithComma(S); - S += ")"; + void printLeft(OutputBuffer &OB) const override { + Callee->print(OB); + OB += "("; + Args.printWithComma(OB); + OB += ")"; } }; @@ -1787,25 +1930,24 @@ public: F(ExprList, Type, InitList, IsGlobal, IsArray); } - void printLeft(OutputStream &S) const override { + void printLeft(OutputBuffer &OB) const override { if (IsGlobal) - S += "::operator "; - S += "new"; + OB += "::operator "; + OB += "new"; if (IsArray) - S += "[]"; - S += ' '; + OB += "[]"; + OB += ' '; if (!ExprList.empty()) { - S += "("; - ExprList.printWithComma(S); - S += ")"; + OB += "("; + ExprList.printWithComma(OB); + OB += ")"; } - Type->print(S); + Type->print(OB); if (!InitList.empty()) { - S += "("; - InitList.printWithComma(S); - S += ")"; + OB += "("; + InitList.printWithComma(OB); + OB += ")"; } - } }; @@ -1820,13 +1962,13 @@ public: template void match(Fn F) const { F(Op, IsGlobal, IsArray); } - void printLeft(OutputStream &S) const override { + void printLeft(OutputBuffer &OB) const override { if (IsGlobal) - S += "::"; - S += "delete"; + OB += "::"; + OB += "delete"; if (IsArray) - S += "[] "; - Op->print(S); + OB += "[] "; + Op->print(OB); } }; @@ -1840,11 +1982,11 @@ public: template void match(Fn F) const { F(Prefix, Child); } - void printLeft(OutputStream &S) const override { - S += Prefix; - S += "("; - Child->print(S); - S += ")"; + void printLeft(OutputBuffer &OB) const override { + OB += Prefix; + OB += "("; + Child->print(OB); + OB += ")"; } }; @@ -1856,9 +1998,9 @@ public: template void match(Fn F) const { F(Number); } - void printLeft(OutputStream &S) const override { - S += "fp"; - S += Number; + void printLeft(OutputBuffer &OB) const override { + OB += "fp"; + OB += Number; } }; @@ -1872,12 +2014,12 @@ public: template void match(Fn F) const { F(Type, Expressions); } - void printLeft(OutputStream &S) const override { - S += "("; - Type->print(S); - S += ")("; - Expressions.printWithComma(S); - S += ")"; + void printLeft(OutputBuffer &OB) const override { + OB += "("; + Type->print(OB); + OB += ")("; + Expressions.printWithComma(OB); + OB += ")"; } }; @@ -1894,12 +2036,12 @@ public: template void match(Fn F) const { F(Type, SubExpr, Offset); } - void printLeft(OutputStream &S) const override { - S += "("; - Type->print(S); - S += ")("; - SubExpr->print(S); - S += ")"; + void printLeft(OutputBuffer &OB) const override { + OB += "("; + Type->print(OB); + OB += ")("; + SubExpr->print(OB); + OB += ")"; } }; @@ -1912,12 +2054,12 @@ public: template void match(Fn F) const { F(Ty, Inits); } - void printLeft(OutputStream &S) const override { + void printLeft(OutputBuffer &OB) const override { if (Ty) - Ty->print(S); - S += '{'; - Inits.printWithComma(S); - S += '}'; + Ty->print(OB); + OB += '{'; + Inits.printWithComma(OB); + OB += '}'; } }; @@ -1931,18 +2073,18 @@ public: template void match(Fn F) const { F(Elem, Init, IsArray); } - void printLeft(OutputStream &S) const override { + void printLeft(OutputBuffer &OB) const override { if (IsArray) { - S += '['; - Elem->print(S); - S += ']'; + OB += '['; + Elem->print(OB); + OB += ']'; } else { - S += '.'; - Elem->print(S); + OB += '.'; + Elem->print(OB); } if (Init->getKind() != KBracedExpr && Init->getKind() != KBracedRangeExpr) - S += " = "; - Init->print(S); + OB += " = "; + Init->print(OB); } }; @@ -1956,15 +2098,15 @@ public: template void match(Fn F) const { F(First, Last, Init); } - void printLeft(OutputStream &S) const override { - S += '['; - First->print(S); - S += " ... "; - Last->print(S); - S += ']'; + void printLeft(OutputBuffer &OB) const override { + OB += '['; + First->print(OB); + OB += " ... "; + Last->print(OB); + OB += ']'; if (Init->getKind() != KBracedExpr && Init->getKind() != KBracedRangeExpr) - S += " = "; - Init->print(S); + OB += " = "; + Init->print(OB); } }; @@ -1983,43 +2125,43 @@ public: F(IsLeftFold, OperatorName, Pack, Init); } - void printLeft(OutputStream &S) const override { + void printLeft(OutputBuffer &OB) const override { auto PrintPack = [&] { - S += '('; - ParameterPackExpansion(Pack).print(S); - S += ')'; + OB += '('; + ParameterPackExpansion(Pack).print(OB); + OB += ')'; }; - S += '('; + OB += '('; if (IsLeftFold) { // init op ... op pack if (Init != nullptr) { - Init->print(S); - S += ' '; - S += OperatorName; - S += ' '; + Init->print(OB); + OB += ' '; + OB += OperatorName; + OB += ' '; } // ... op pack - S += "... "; - S += OperatorName; - S += ' '; + OB += "... "; + OB += OperatorName; + OB += ' '; PrintPack(); } else { // !IsLeftFold // pack op ... PrintPack(); - S += ' '; - S += OperatorName; - S += " ..."; + OB += ' '; + OB += OperatorName; + OB += " ..."; // pack op ... op init if (Init != nullptr) { - S += ' '; - S += OperatorName; - S += ' '; - Init->print(S); + OB += ' '; + OB += OperatorName; + OB += ' '; + Init->print(OB); } } - S += ')'; + OB += ')'; } }; @@ -2031,9 +2173,9 @@ public: template void match(Fn F) const { F(Op); } - void printLeft(OutputStream &S) const override { - S += "throw "; - Op->print(S); + void printLeft(OutputBuffer &OB) const override { + OB += "throw "; + Op->print(OB); } }; @@ -2045,8 +2187,8 @@ public: template void match(Fn F) const { F(Value); } - void printLeft(OutputStream &S) const override { - S += Value ? StringView("true") : StringView("false"); + void printLeft(OutputBuffer &OB) const override { + OB += Value ? StringView("true") : StringView("false"); } }; @@ -2058,10 +2200,10 @@ public: template void match(Fn F) const { F(Type); } - void printLeft(OutputStream &S) const override { - S += "\"<"; - Type->print(S); - S += ">\""; + void printLeft(OutputBuffer &OB) const override { + OB += "\"<"; + Type->print(OB); + OB += ">\""; } }; @@ -2073,11 +2215,11 @@ public: template void match(Fn F) const { F(Type); } - void printLeft(OutputStream &S) const override { - S += "[]"; + void printLeft(OutputBuffer &OB) const override { + OB += "[]"; if (Type->getKind() == KClosureTypeName) - static_cast(Type)->printDeclarator(S); - S += "{...}"; + static_cast(Type)->printDeclarator(OB); + OB += "{...}"; } }; @@ -2092,15 +2234,15 @@ public: template void match(Fn F) const { F(Ty, Integer); } - void printLeft(OutputStream &S) const override { - S << "("; - Ty->print(S); - S << ")"; + void printLeft(OutputBuffer &OB) const override { + OB << "("; + Ty->print(OB); + OB << ")"; if (Integer[0] == 'n') - S << "-" << Integer.dropFront(1); + OB << "-" << Integer.dropFront(1); else - S << Integer; + OB << Integer; } }; @@ -2114,21 +2256,21 @@ public: template void match(Fn F) const { F(Type, Value); } - void printLeft(OutputStream &S) const override { + void printLeft(OutputBuffer &OB) const override { if (Type.size() > 3) { - S += "("; - S += Type; - S += ")"; + OB += "("; + OB += Type; + OB += ")"; } if (Value[0] == 'n') { - S += "-"; - S += Value.dropFront(1); + OB += "-"; + OB += Value.dropFront(1); } else - S += Value; + OB += Value; if (Type.size() <= 3) - S += Type; + OB += Type; } }; @@ -2158,7 +2300,7 @@ public: template void match(Fn F) const { F(Contents); } - void printLeft(OutputStream &s) const override { + void printLeft(OutputBuffer &OB) const override { const char *first = Contents.begin(); const char *last = Contents.end() + 1; @@ -2184,7 +2326,7 @@ public: #endif char num[FloatData::max_demangled_size] = {0}; int n = snprintf(num, sizeof(num), FloatData::spec, value); - s += StringView(num, num + n); + OB += StringView(num, num + n); } } }; @@ -2217,125 +2359,6 @@ FOR_EACH_NODE_KIND(SPECIALIZATION) #undef FOR_EACH_NODE_KIND -template -class PODSmallVector { - static_assert(std::is_pod::value, - "T is required to be a plain old data type"); - - T* First = nullptr; - T* Last = nullptr; - T* Cap = nullptr; - T Inline[N] = {0}; - - bool isInline() const { return First == Inline; } - - void clearInline() { - First = Inline; - Last = Inline; - Cap = Inline + N; - } - - void reserve(size_t NewCap) { - size_t S = size(); - if (isInline()) { - auto* Tmp = static_cast(std::malloc(NewCap * sizeof(T))); - if (Tmp == nullptr) - std::terminate(); - std::copy(First, Last, Tmp); - First = Tmp; - } else { - First = static_cast(std::realloc(First, NewCap * sizeof(T))); - if (First == nullptr) - std::terminate(); - } - Last = First + S; - Cap = First + NewCap; - } - -public: - PODSmallVector() : First(Inline), Last(First), Cap(Inline + N) {} - - PODSmallVector(const PODSmallVector&) = delete; - PODSmallVector& operator=(const PODSmallVector&) = delete; - - PODSmallVector(PODSmallVector&& Other) : PODSmallVector() { - if (Other.isInline()) { - std::copy(Other.begin(), Other.end(), First); - Last = First + Other.size(); - Other.clear(); - return; - } - - First = Other.First; - Last = Other.Last; - Cap = Other.Cap; - Other.clearInline(); - } - - PODSmallVector& operator=(PODSmallVector&& Other) { - if (Other.isInline()) { - if (!isInline()) { - std::free(First); - clearInline(); - } - std::copy(Other.begin(), Other.end(), First); - Last = First + Other.size(); - Other.clear(); - return *this; - } - - if (isInline()) { - First = Other.First; - Last = Other.Last; - Cap = Other.Cap; - Other.clearInline(); - return *this; - } - - std::swap(First, Other.First); - std::swap(Last, Other.Last); - std::swap(Cap, Other.Cap); - Other.clear(); - return *this; - } - - void push_back(const T& Elem) { - if (Last == Cap) - reserve(size() * 2); - *Last++ = Elem; - } - - void pop_back() { - assert(Last != First && "Popping empty vector!"); - --Last; - } - - void dropBack(size_t Index) { - assert(Index <= size() && "dropBack() can't expand!"); - Last = First + Index; - } - - T* begin() { return First; } - T* end() { return Last; } - - bool empty() const { return First == Last; } - size_t size() const { return static_cast(Last - First); } - T& back() { - assert(Last != First && "Calling back() on empty vector!"); - return *(Last - 1); - } - T& operator[](size_t Index) { - assert(Index < size() && "Invalid access!"); - return *(begin() + Index); - } - void clear() { Last = First; } - - ~PODSmallVector() { - if (!isInline()) - std::free(First); - } -}; - template struct AbstractManglingParser { const char *First; const char *Last; @@ -3884,6 +3907,16 @@ Node *AbstractManglingParser::parseType() { case 'h': First += 2; return make("half"); + // ::= DF _ # ISO/IEC TS 18661 binary floating point (N bits) + case 'F': { + First += 2; + Node *DimensionNumber = make(parseNumber()); + if (!DimensionNumber) + return nullptr; + if (!consumeIf('_')) + return nullptr; + return make(DimensionNumber); + } // ::= Di # char32_t case 'i': First += 2; diff --git a/llvm/include/llvm/Demangle/MicrosoftDemangleNodes.h b/llvm/include/llvm/Demangle/MicrosoftDemangleNodes.h index 77446e9b0f07..46daa3885a06 100644 --- a/llvm/include/llvm/Demangle/MicrosoftDemangleNodes.h +++ b/llvm/include/llvm/Demangle/MicrosoftDemangleNodes.h @@ -21,11 +21,11 @@ namespace llvm { namespace itanium_demangle { -class OutputStream; +class OutputBuffer; } } -using llvm::itanium_demangle::OutputStream; +using llvm::itanium_demangle::OutputBuffer; using llvm::itanium_demangle::StringView; namespace llvm { @@ -80,6 +80,7 @@ enum OutputFlags { OF_NoAccessSpecifier = 4, OF_NoMemberType = 8, OF_NoReturnType = 16, + OF_NoVariableType = 32, }; // Types @@ -261,7 +262,7 @@ struct Node { NodeKind kind() const { return Kind; } - virtual void output(OutputStream &OS, OutputFlags Flags) const = 0; + virtual void output(OutputBuffer &OB, OutputFlags Flags) const = 0; std::string toString(OutputFlags Flags = OF_Default) const; @@ -300,12 +301,12 @@ struct SpecialTableSymbolNode; struct TypeNode : public Node { explicit TypeNode(NodeKind K) : Node(K) {} - virtual void outputPre(OutputStream &OS, OutputFlags Flags) const = 0; - virtual void outputPost(OutputStream &OS, OutputFlags Flags) const = 0; + virtual void outputPre(OutputBuffer &OB, OutputFlags Flags) const = 0; + virtual void outputPost(OutputBuffer &OB, OutputFlags Flags) const = 0; - void output(OutputStream &OS, OutputFlags Flags) const override { - outputPre(OS, Flags); - outputPost(OS, Flags); + void output(OutputBuffer &OB, OutputFlags Flags) const override { + outputPre(OB, Flags); + outputPost(OB, Flags); } Qualifiers Quals = Q_None; @@ -315,8 +316,8 @@ struct PrimitiveTypeNode : public TypeNode { explicit PrimitiveTypeNode(PrimitiveKind K) : TypeNode(NodeKind::PrimitiveType), PrimKind(K) {} - void outputPre(OutputStream &OS, OutputFlags Flags) const override; - void outputPost(OutputStream &OS, OutputFlags Flags) const override {} + void outputPre(OutputBuffer &OB, OutputFlags Flags) const override; + void outputPost(OutputBuffer &OB, OutputFlags Flags) const override {} PrimitiveKind PrimKind; }; @@ -325,8 +326,8 @@ struct FunctionSignatureNode : public TypeNode { explicit FunctionSignatureNode(NodeKind K) : TypeNode(K) {} FunctionSignatureNode() : TypeNode(NodeKind::FunctionSignature) {} - void outputPre(OutputStream &OS, OutputFlags Flags) const override; - void outputPost(OutputStream &OS, OutputFlags Flags) const override; + void outputPre(OutputBuffer &OB, OutputFlags Flags) const override; + void outputPost(OutputBuffer &OB, OutputFlags Flags) const override; // Valid if this FunctionTypeNode is the Pointee of a PointerType or // MemberPointerType. @@ -359,13 +360,13 @@ struct IdentifierNode : public Node { NodeArrayNode *TemplateParams = nullptr; protected: - void outputTemplateParameters(OutputStream &OS, OutputFlags Flags) const; + void outputTemplateParameters(OutputBuffer &OB, OutputFlags Flags) const; }; struct VcallThunkIdentifierNode : public IdentifierNode { VcallThunkIdentifierNode() : IdentifierNode(NodeKind::VcallThunkIdentifier) {} - void output(OutputStream &OS, OutputFlags Flags) const override; + void output(OutputBuffer &OB, OutputFlags Flags) const override; uint64_t OffsetInVTable = 0; }; @@ -374,7 +375,7 @@ struct DynamicStructorIdentifierNode : public IdentifierNode { DynamicStructorIdentifierNode() : IdentifierNode(NodeKind::DynamicStructorIdentifier) {} - void output(OutputStream &OS, OutputFlags Flags) const override; + void output(OutputBuffer &OB, OutputFlags Flags) const override; VariableSymbolNode *Variable = nullptr; QualifiedNameNode *Name = nullptr; @@ -384,7 +385,7 @@ struct DynamicStructorIdentifierNode : public IdentifierNode { struct NamedIdentifierNode : public IdentifierNode { NamedIdentifierNode() : IdentifierNode(NodeKind::NamedIdentifier) {} - void output(OutputStream &OS, OutputFlags Flags) const override; + void output(OutputBuffer &OB, OutputFlags Flags) const override; StringView Name; }; @@ -394,7 +395,7 @@ struct IntrinsicFunctionIdentifierNode : public IdentifierNode { : IdentifierNode(NodeKind::IntrinsicFunctionIdentifier), Operator(Operator) {} - void output(OutputStream &OS, OutputFlags Flags) const override; + void output(OutputBuffer &OB, OutputFlags Flags) const override; IntrinsicFunctionKind Operator; }; @@ -403,7 +404,7 @@ struct LiteralOperatorIdentifierNode : public IdentifierNode { LiteralOperatorIdentifierNode() : IdentifierNode(NodeKind::LiteralOperatorIdentifier) {} - void output(OutputStream &OS, OutputFlags Flags) const override; + void output(OutputBuffer &OB, OutputFlags Flags) const override; StringView Name; }; @@ -412,7 +413,7 @@ struct LocalStaticGuardIdentifierNode : public IdentifierNode { LocalStaticGuardIdentifierNode() : IdentifierNode(NodeKind::LocalStaticGuardIdentifier) {} - void output(OutputStream &OS, OutputFlags Flags) const override; + void output(OutputBuffer &OB, OutputFlags Flags) const override; bool IsThread = false; uint32_t ScopeIndex = 0; @@ -422,7 +423,7 @@ struct ConversionOperatorIdentifierNode : public IdentifierNode { ConversionOperatorIdentifierNode() : IdentifierNode(NodeKind::ConversionOperatorIdentifier) {} - void output(OutputStream &OS, OutputFlags Flags) const override; + void output(OutputBuffer &OB, OutputFlags Flags) const override; // The type that this operator converts too. TypeNode *TargetType = nullptr; @@ -434,7 +435,7 @@ struct StructorIdentifierNode : public IdentifierNode { : IdentifierNode(NodeKind::StructorIdentifier), IsDestructor(IsDestructor) {} - void output(OutputStream &OS, OutputFlags Flags) const override; + void output(OutputBuffer &OB, OutputFlags Flags) const override; // The name of the class that this is a structor of. IdentifierNode *Class = nullptr; @@ -444,8 +445,8 @@ struct StructorIdentifierNode : public IdentifierNode { struct ThunkSignatureNode : public FunctionSignatureNode { ThunkSignatureNode() : FunctionSignatureNode(NodeKind::ThunkSignature) {} - void outputPre(OutputStream &OS, OutputFlags Flags) const override; - void outputPost(OutputStream &OS, OutputFlags Flags) const override; + void outputPre(OutputBuffer &OB, OutputFlags Flags) const override; + void outputPost(OutputBuffer &OB, OutputFlags Flags) const override; struct ThisAdjustor { uint32_t StaticOffset = 0; @@ -459,8 +460,8 @@ struct ThunkSignatureNode : public FunctionSignatureNode { struct PointerTypeNode : public TypeNode { PointerTypeNode() : TypeNode(NodeKind::PointerType) {} - void outputPre(OutputStream &OS, OutputFlags Flags) const override; - void outputPost(OutputStream &OS, OutputFlags Flags) const override; + void outputPre(OutputBuffer &OB, OutputFlags Flags) const override; + void outputPost(OutputBuffer &OB, OutputFlags Flags) const override; // Is this a pointer, reference, or rvalue-reference? PointerAffinity Affinity = PointerAffinity::None; @@ -476,8 +477,8 @@ struct PointerTypeNode : public TypeNode { struct TagTypeNode : public TypeNode { explicit TagTypeNode(TagKind Tag) : TypeNode(NodeKind::TagType), Tag(Tag) {} - void outputPre(OutputStream &OS, OutputFlags Flags) const override; - void outputPost(OutputStream &OS, OutputFlags Flags) const override; + void outputPre(OutputBuffer &OB, OutputFlags Flags) const override; + void outputPost(OutputBuffer &OB, OutputFlags Flags) const override; QualifiedNameNode *QualifiedName = nullptr; TagKind Tag; @@ -486,11 +487,11 @@ struct TagTypeNode : public TypeNode { struct ArrayTypeNode : public TypeNode { ArrayTypeNode() : TypeNode(NodeKind::ArrayType) {} - void outputPre(OutputStream &OS, OutputFlags Flags) const override; - void outputPost(OutputStream &OS, OutputFlags Flags) const override; + void outputPre(OutputBuffer &OB, OutputFlags Flags) const override; + void outputPost(OutputBuffer &OB, OutputFlags Flags) const override; - void outputDimensionsImpl(OutputStream &OS, OutputFlags Flags) const; - void outputOneDimension(OutputStream &OS, OutputFlags Flags, Node *N) const; + void outputDimensionsImpl(OutputBuffer &OB, OutputFlags Flags) const; + void outputOneDimension(OutputBuffer &OB, OutputFlags Flags, Node *N) const; // A list of array dimensions. e.g. [3,4,5] in `int Foo[3][4][5]` NodeArrayNode *Dimensions = nullptr; @@ -501,14 +502,14 @@ struct ArrayTypeNode : public TypeNode { struct IntrinsicNode : public TypeNode { IntrinsicNode() : TypeNode(NodeKind::IntrinsicType) {} - void output(OutputStream &OS, OutputFlags Flags) const override {} + void output(OutputBuffer &OB, OutputFlags Flags) const override {} }; struct CustomTypeNode : public TypeNode { CustomTypeNode() : TypeNode(NodeKind::Custom) {} - void outputPre(OutputStream &OS, OutputFlags Flags) const override; - void outputPost(OutputStream &OS, OutputFlags Flags) const override; + void outputPre(OutputBuffer &OB, OutputFlags Flags) const override; + void outputPost(OutputBuffer &OB, OutputFlags Flags) const override; IdentifierNode *Identifier = nullptr; }; @@ -516,9 +517,9 @@ struct CustomTypeNode : public TypeNode { struct NodeArrayNode : public Node { NodeArrayNode() : Node(NodeKind::NodeArray) {} - void output(OutputStream &OS, OutputFlags Flags) const override; + void output(OutputBuffer &OB, OutputFlags Flags) const override; - void output(OutputStream &OS, OutputFlags Flags, StringView Separator) const; + void output(OutputBuffer &OB, OutputFlags Flags, StringView Separator) const; Node **Nodes = nullptr; size_t Count = 0; @@ -527,7 +528,7 @@ struct NodeArrayNode : public Node { struct QualifiedNameNode : public Node { QualifiedNameNode() : Node(NodeKind::QualifiedName) {} - void output(OutputStream &OS, OutputFlags Flags) const override; + void output(OutputBuffer &OB, OutputFlags Flags) const override; NodeArrayNode *Components = nullptr; @@ -541,7 +542,7 @@ struct TemplateParameterReferenceNode : public Node { TemplateParameterReferenceNode() : Node(NodeKind::TemplateParameterReference) {} - void output(OutputStream &OS, OutputFlags Flags) const override; + void output(OutputBuffer &OB, OutputFlags Flags) const override; SymbolNode *Symbol = nullptr; @@ -556,7 +557,7 @@ struct IntegerLiteralNode : public Node { IntegerLiteralNode(uint64_t Value, bool IsNegative) : Node(NodeKind::IntegerLiteral), Value(Value), IsNegative(IsNegative) {} - void output(OutputStream &OS, OutputFlags Flags) const override; + void output(OutputBuffer &OB, OutputFlags Flags) const override; uint64_t Value = 0; bool IsNegative = false; @@ -566,7 +567,7 @@ struct RttiBaseClassDescriptorNode : public IdentifierNode { RttiBaseClassDescriptorNode() : IdentifierNode(NodeKind::RttiBaseClassDescriptor) {} - void output(OutputStream &OS, OutputFlags Flags) const override; + void output(OutputBuffer &OB, OutputFlags Flags) const override; uint32_t NVOffset = 0; int32_t VBPtrOffset = 0; @@ -576,7 +577,7 @@ struct RttiBaseClassDescriptorNode : public IdentifierNode { struct SymbolNode : public Node { explicit SymbolNode(NodeKind K) : Node(K) {} - void output(OutputStream &OS, OutputFlags Flags) const override; + void output(OutputBuffer &OB, OutputFlags Flags) const override; QualifiedNameNode *Name = nullptr; }; @@ -584,7 +585,7 @@ struct SpecialTableSymbolNode : public SymbolNode { explicit SpecialTableSymbolNode() : SymbolNode(NodeKind::SpecialTableSymbol) {} - void output(OutputStream &OS, OutputFlags Flags) const override; + void output(OutputBuffer &OB, OutputFlags Flags) const override; QualifiedNameNode *TargetName = nullptr; Qualifiers Quals = Qualifiers::Q_None; }; @@ -593,7 +594,7 @@ struct LocalStaticGuardVariableNode : public SymbolNode { LocalStaticGuardVariableNode() : SymbolNode(NodeKind::LocalStaticGuardVariable) {} - void output(OutputStream &OS, OutputFlags Flags) const override; + void output(OutputBuffer &OB, OutputFlags Flags) const override; bool IsVisible = false; }; @@ -601,7 +602,7 @@ struct LocalStaticGuardVariableNode : public SymbolNode { struct EncodedStringLiteralNode : public SymbolNode { EncodedStringLiteralNode() : SymbolNode(NodeKind::EncodedStringLiteral) {} - void output(OutputStream &OS, OutputFlags Flags) const override; + void output(OutputBuffer &OB, OutputFlags Flags) const override; StringView DecodedString; bool IsTruncated = false; @@ -611,7 +612,7 @@ struct EncodedStringLiteralNode : public SymbolNode { struct VariableSymbolNode : public SymbolNode { VariableSymbolNode() : SymbolNode(NodeKind::VariableSymbol) {} - void output(OutputStream &OS, OutputFlags Flags) const override; + void output(OutputBuffer &OB, OutputFlags Flags) const override; StorageClass SC = StorageClass::None; TypeNode *Type = nullptr; @@ -620,7 +621,7 @@ struct VariableSymbolNode : public SymbolNode { struct FunctionSymbolNode : public SymbolNode { FunctionSymbolNode() : SymbolNode(NodeKind::FunctionSymbol) {} - void output(OutputStream &OS, OutputFlags Flags) const override; + void output(OutputBuffer &OB, OutputFlags Flags) const override; FunctionSignatureNode *Signature = nullptr; }; diff --git a/llvm/include/llvm/Demangle/Utility.h b/llvm/include/llvm/Demangle/Utility.h index 04ff65a35aed..4fea9351a4bf 100644 --- a/llvm/include/llvm/Demangle/Utility.h +++ b/llvm/include/llvm/Demangle/Utility.h @@ -24,7 +24,7 @@ DEMANGLE_NAMESPACE_BEGIN // Stream that AST nodes write their string representation into after the AST // has been parsed. -class OutputStream { +class OutputBuffer { char *Buffer = nullptr; size_t CurrentPosition = 0; size_t BufferCapacity = 0; @@ -63,9 +63,9 @@ class OutputStream { } public: - OutputStream(char *StartBuf, size_t Size) + OutputBuffer(char *StartBuf, size_t Size) : Buffer(StartBuf), CurrentPosition(0), BufferCapacity(Size) {} - OutputStream() = default; + OutputBuffer() = default; void reset(char *Buffer_, size_t BufferCapacity_) { CurrentPosition = 0; Buffer = Buffer_; @@ -77,7 +77,7 @@ public: unsigned CurrentPackIndex = std::numeric_limits::max(); unsigned CurrentPackMax = std::numeric_limits::max(); - OutputStream &operator+=(StringView R) { + OutputBuffer &operator+=(StringView R) { size_t Size = R.size(); if (Size == 0) return *this; @@ -87,17 +87,28 @@ public: return *this; } - OutputStream &operator+=(char C) { + OutputBuffer &operator+=(char C) { grow(1); Buffer[CurrentPosition++] = C; return *this; } - OutputStream &operator<<(StringView R) { return (*this += R); } + OutputBuffer &operator<<(StringView R) { return (*this += R); } - OutputStream &operator<<(char C) { return (*this += C); } + OutputBuffer prepend(StringView R) { + size_t Size = R.size(); + + grow(Size); + std::memmove(Buffer + Size, Buffer, CurrentPosition); + std::memcpy(Buffer, R.begin(), Size); + CurrentPosition += Size; - OutputStream &operator<<(long long N) { + return *this; + } + + OutputBuffer &operator<<(char C) { return (*this += C); } + + OutputBuffer &operator<<(long long N) { if (N < 0) writeUnsigned(static_cast(-N), true); else @@ -105,27 +116,37 @@ public: return *this; } - OutputStream &operator<<(unsigned long long N) { + OutputBuffer &operator<<(unsigned long long N) { writeUnsigned(N, false); return *this; } - OutputStream &operator<<(long N) { + OutputBuffer &operator<<(long N) { return this->operator<<(static_cast(N)); } - OutputStream &operator<<(unsigned long N) { + OutputBuffer &operator<<(unsigned long N) { return this->operator<<(static_cast(N)); } - OutputStream &operator<<(int N) { + OutputBuffer &operator<<(int N) { return this->operator<<(static_cast(N)); } - OutputStream &operator<<(unsigned int N) { + OutputBuffer &operator<<(unsigned int N) { return this->operator<<(static_cast(N)); } + void insert(size_t Pos, const char *S, size_t N) { + assert(Pos <= CurrentPosition); + if (N == 0) + return; + grow(N); + std::memmove(Buffer + Pos + N, Buffer + Pos, CurrentPosition - Pos); + std::memcpy(Buffer + Pos, S, N); + CurrentPosition += N; + } + size_t getCurrentPosition() const { return CurrentPosition; } void setCurrentPosition(size_t NewPos) { CurrentPosition = NewPos; } @@ -171,7 +192,7 @@ public: SwapAndRestore &operator=(const SwapAndRestore &) = delete; }; -inline bool initializeOutputStream(char *Buf, size_t *N, OutputStream &S, +inline bool initializeOutputBuffer(char *Buf, size_t *N, OutputBuffer &OB, size_t InitSize) { size_t BufferSize; if (Buf == nullptr) { @@ -182,7 +203,7 @@ inline bool initializeOutputStream(char *Buf, size_t *N, OutputStream &S, } else BufferSize = *N; - S.reset(Buf, BufferSize); + OB.reset(Buf, BufferSize); return true; } diff --git a/llvm/include/llvm/ExecutionEngine/ExecutionEngine.h b/llvm/include/llvm/ExecutionEngine/ExecutionEngine.h index 2e386518f0bf..43c91fb5f988 100644 --- a/llvm/include/llvm/ExecutionEngine/ExecutionEngine.h +++ b/llvm/include/llvm/ExecutionEngine/ExecutionEngine.h @@ -21,7 +21,6 @@ #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/ExecutionEngine/JITSymbol.h" -#include "llvm/ExecutionEngine/OrcV1Deprecation.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/Module.h" #include "llvm/Object/Binary.h" diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/ELF_aarch64.h b/llvm/include/llvm/ExecutionEngine/JITLink/ELF_aarch64.h new file mode 100644 index 000000000000..50eb598139ea --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/JITLink/ELF_aarch64.h @@ -0,0 +1,39 @@ +//===--- ELF_aarch64.h - JIT link functions for ELF/aarch64 --*- C++ -*----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +//===----------------------------------------------------------------------===// +// +// jit-link functions for ELF/aarch64. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_JITLINK_ELF_AARCH64_H +#define LLVM_EXECUTIONENGINE_JITLINK_ELF_AARCH64_H + +#include "llvm/ExecutionEngine/JITLink/JITLink.h" + +namespace llvm { +namespace jitlink { + +/// Create a LinkGraph from an ELF/aarch64 relocatable object +/// +/// Note: The graph does not take ownership of the underlying buffer, nor copy +/// its contents. The caller is responsible for ensuring that the object buffer +/// outlives the graph. +Expected> +createLinkGraphFromELFObject_aarch64(MemoryBufferRef ObjectBuffer); + +/// jit-link the given object buffer, which must be a ELF aarch64 relocatable +/// object file. +void link_ELF_aarch64(std::unique_ptr G, + std::unique_ptr Ctx); + +} // end namespace jitlink +} // end namespace llvm + +#endif // LLVM_EXECUTIONENGINE_JITLINK_ELF_AARCH64_H diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/ELF_riscv.h b/llvm/include/llvm/ExecutionEngine/JITLink/ELF_riscv.h index 1339ab51cbb9..5a8b186a2c3e 100644 --- a/llvm/include/llvm/ExecutionEngine/JITLink/ELF_riscv.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/ELF_riscv.h @@ -35,4 +35,4 @@ void link_ELF_riscv(std::unique_ptr G, } // end namespace jitlink } // end namespace llvm -#endif // LLVM_EXECUTIONENGINE_JITLINK_ELF_RISCV64_H +#endif // LLVM_EXECUTIONENGINE_JITLINK_ELF_RISCV_H diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/ELF_x86_64.h b/llvm/include/llvm/ExecutionEngine/JITLink/ELF_x86_64.h index d8ed953363e6..f5fa9e96c594 100644 --- a/llvm/include/llvm/ExecutionEngine/JITLink/ELF_x86_64.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/ELF_x86_64.h @@ -21,29 +21,17 @@ namespace jitlink { namespace ELF_x86_64_Edges { enum ELFX86RelocationKind : Edge::Kind { Branch32 = Edge::FirstRelocation, - Branch32ToStub, - Pointer32, + Pointer32Signed, Pointer64, - Pointer64Anon, PCRel32, - PCRel64, - PCRel32Minus1, - PCRel32Minus2, - PCRel32Minus4, - PCRel32Anon, - PCRel32Minus1Anon, - PCRel32Minus2Anon, - PCRel32Minus4Anon, PCRel32GOTLoad, - PCRel32GOT, + PCRel32GOTLoadRelaxable, + PCRel32REXGOTLoadRelaxable, + PCRel32TLV, PCRel64GOT, GOTOFF64, GOT64, - PCRel32TLV, - Delta32, Delta64, - NegDelta32, - NegDelta64, }; } // end namespace ELF_x86_64_Edges diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h b/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h index 6162a675ec12..83d85953fce6 100644 --- a/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h @@ -13,19 +13,19 @@ #ifndef LLVM_EXECUTIONENGINE_JITLINK_JITLINK_H #define LLVM_EXECUTIONENGINE_JITLINK_JITLINK_H -#include "JITLinkMemoryManager.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Triple.h" +#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h" +#include "llvm/ExecutionEngine/JITLink/MemoryFlags.h" #include "llvm/ExecutionEngine/JITSymbol.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/MathExtras.h" -#include "llvm/Support/Memory.h" #include "llvm/Support/MemoryBuffer.h" #include @@ -225,7 +225,7 @@ public: /// Get the content for this block. Block must not be a zero-fill block. ArrayRef getContent() const { - assert(Data && "Section does not contain content"); + assert(Data && "Block does not contain content"); return ArrayRef(Data, Size); } @@ -233,6 +233,7 @@ public: /// Caller is responsible for ensuring the underlying bytes are not /// deallocated while pointed to by this block. void setContent(ArrayRef Content) { + assert(Content.data() && "Setting null content"); Data = Content.data(); Size = Content.size(); ContentMutable = false; @@ -251,6 +252,7 @@ public: /// to call this on a block with immutable content -- consider using /// getMutableContent instead. MutableArrayRef getAlreadyMutableContent() { + assert(Data && "Block does not contain content"); assert(ContentMutable && "Content is not mutable"); return MutableArrayRef(const_cast(Data), Size); } @@ -260,6 +262,7 @@ public: /// The caller is responsible for ensuring that the memory pointed to by /// MutableContent is not deallocated while pointed to by this block. void setMutableContent(MutableArrayRef MutableContent) { + assert(MutableContent.data() && "Setting null content"); Data = MutableContent.data(); Size = MutableContent.size(); ContentMutable = true; @@ -295,6 +298,7 @@ public: /// Add an edge to this block. void addEdge(Edge::Kind K, Edge::OffsetT Offset, Symbol &Target, Edge::AddendT Addend) { + assert(!isZeroFill() && "Adding edge to zero-fill block?"); Edges.push_back(Edge(K, Offset, Target, Addend)); } @@ -339,6 +343,12 @@ private: std::vector Edges; }; +// Align a JITTargetAddress to conform with block alignment requirements. +inline JITTargetAddress alignToBlock(JITTargetAddress Addr, Block &B) { + uint64_t Delta = (B.getAlignmentOffset() - Addr) % B.getAlignment(); + return Addr + Delta; +} + /// Describes symbol linkage. This can be used to make resolve definition /// clashes. enum class Linkage : uint8_t { @@ -640,8 +650,7 @@ class Section { friend class LinkGraph; private: - Section(StringRef Name, sys::Memory::ProtectionFlags Prot, - SectionOrdinal SecOrdinal) + Section(StringRef Name, MemProt Prot, SectionOrdinal SecOrdinal) : Name(Name), Prot(Prot), SecOrdinal(SecOrdinal) {} using SymbolSet = DenseSet; @@ -666,12 +675,16 @@ public: StringRef getName() const { return Name; } /// Returns the protection flags for this section. - sys::Memory::ProtectionFlags getProtectionFlags() const { return Prot; } + MemProt getMemProt() const { return Prot; } /// Set the protection flags for this section. - void setProtectionFlags(sys::Memory::ProtectionFlags Prot) { - this->Prot = Prot; - } + void setMemProt(MemProt Prot) { this->Prot = Prot; } + + /// Get the deallocation policy for this section. + MemDeallocPolicy getMemDeallocPolicy() const { return MDP; } + + /// Set the deallocation policy for this section. + void setMemDeallocPolicy(MemDeallocPolicy MDP) { this->MDP = MDP; } /// Returns the ordinal for this section. SectionOrdinal getOrdinal() const { return SecOrdinal; } @@ -686,6 +699,7 @@ public: return make_range(Blocks.begin(), Blocks.end()); } + /// Returns the number of blocks in this section. BlockSet::size_type blocks_size() const { return Blocks.size(); } /// Returns an iterator over the symbols defined in this section. @@ -734,7 +748,8 @@ private: } StringRef Name; - sys::Memory::ProtectionFlags Prot; + MemProt Prot; + MemDeallocPolicy MDP = MemDeallocPolicy::Standard; SectionOrdinal SecOrdinal = 0; BlockSet Blocks; SymbolSet Symbols; @@ -916,6 +931,11 @@ public: : Name(std::move(Name)), TT(TT), PointerSize(PointerSize), Endianness(Endianness), GetEdgeKindName(std::move(GetEdgeKindName)) {} + LinkGraph(const LinkGraph &) = delete; + LinkGraph &operator=(const LinkGraph &) = delete; + LinkGraph(LinkGraph &&) = delete; + LinkGraph &operator=(LinkGraph &&) = delete; + /// Returns the name of this graph (usually the name of the original /// underlying MemoryBuffer). const std::string &getName() const { return Name; } @@ -962,7 +982,7 @@ public: } /// Create a section with the given name, protection flags, and alignment. - Section &createSection(StringRef Name, sys::Memory::ProtectionFlags Prot) { + Section &createSection(StringRef Name, MemProt Prot) { assert(llvm::find_if(Sections, [&](std::unique_ptr
&Sec) { return Sec->getName() == Name; @@ -1100,10 +1120,10 @@ public: Symbol &addDefinedSymbol(Block &Content, JITTargetAddress Offset, StringRef Name, JITTargetAddress Size, Linkage L, Scope S, bool IsCallable, bool IsLive) { - assert(llvm::count_if(defined_symbols(), - [&](const Symbol *Sym) { - return Sym->getName() == Name; - }) == 0 && + assert((S == Scope::Local || llvm::count_if(defined_symbols(), + [&](const Symbol *Sym) { + return Sym->getName() == Name; + }) == 0) && "Duplicate defined symbol"); auto &Sym = Symbol::constructNamedDef(Allocator.Allocate(), Content, Offset, @@ -1237,6 +1257,7 @@ public: void transferDefinedSymbol(Symbol &Sym, Block &DestBlock, JITTargetAddress NewOffset, Optional ExplicitNewSize) { + auto &OldSection = Sym.getBlock().getSection(); Sym.setBlock(DestBlock); Sym.setOffset(NewOffset); if (ExplicitNewSize) @@ -1246,6 +1267,10 @@ public: if (Sym.getSize() > RemainingBlockSize) Sym.setSize(RemainingBlockSize); } + if (&DestBlock.getSection() != &OldSection) { + OldSection.removeSymbol(Sym); + DestBlock.getSection().addSymbol(Sym); + } } /// Transfers the given Block and all Symbols pointing to it to the given @@ -1280,6 +1305,8 @@ public: bool PreserveSrcSection = false) { if (&DstSection == &SrcSection) return; + for (auto *B : SrcSection.blocks()) + B->setSection(DstSection); SrcSection.transferContentTo(DstSection); if (!PreserveSrcSection) removeSection(SrcSection); @@ -1345,6 +1372,13 @@ public: Sections.erase(I); } + /// Accessor for the AllocActions object for this graph. This can be used to + /// register allocation action calls prior to finalization. + /// + /// Accessing this object after finalization will result in undefined + /// behavior. + JITLinkMemoryManager::AllocActions &allocActions() { return AAs; } + /// Dump the graph. void dump(raw_ostream &OS); @@ -1361,6 +1395,7 @@ private: SectionList Sections; ExternalSymbolSet ExternalSymbols; ExternalSymbolSet AbsoluteSymbols; + JITLinkMemoryManager::AllocActions AAs; }; inline MutableArrayRef Block::getMutableContent(LinkGraph &G) { @@ -1650,8 +1685,7 @@ public: /// finalized (i.e. emitted to memory and memory permissions set). If all of /// this objects dependencies have also been finalized then the code is ready /// to run. - virtual void - notifyFinalized(std::unique_ptr A) = 0; + virtual void notifyFinalized(JITLinkMemoryManager::FinalizedAlloc Alloc) = 0; /// Called by JITLink prior to linking to determine whether default passes for /// the target should be added. The default implementation returns true. @@ -1683,6 +1717,36 @@ Error markAllSymbolsLive(LinkGraph &G); Error makeTargetOutOfRangeError(const LinkGraph &G, const Block &B, const Edge &E); +/// Base case for edge-visitors where the visitor-list is empty. +inline void visitEdge(LinkGraph &G, Block *B, Edge &E) {} + +/// Applies the first visitor in the list to the given edge. If the visitor's +/// visitEdge method returns true then we return immediately, otherwise we +/// apply the next visitor. +template +void visitEdge(LinkGraph &G, Block *B, Edge &E, VisitorT &&V, + VisitorTs &&...Vs) { + if (!V.visitEdge(G, B, E)) + visitEdge(G, B, E, std::forward(Vs)...); +} + +/// For each edge in the given graph, apply a list of visitors to the edge, +/// stopping when the first visitor's visitEdge method returns true. +/// +/// Only visits edges that were in the graph at call time: if any visitor +/// adds new edges those will not be visited. Visitors are not allowed to +/// remove edges (though they can change their kind, target, and addend). +template +void visitExistingEdges(LinkGraph &G, VisitorTs &&...Vs) { + // We may add new blocks during this process, but we don't want to iterate + // over them, so build a worklist. + std::vector Worklist(G.blocks().begin(), G.blocks().end()); + + for (auto *B : Worklist) + for (auto &E : B->edges()) + visitEdge(G, B, E, std::forward(Vs)...); +} + /// Create a LinkGraph from the given object buffer. /// /// Note: The graph does not take ownership of the underlying buffer, nor copy diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h b/llvm/include/llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h index cee7d6b09c48..62c271dfc0b2 100644 --- a/llvm/include/llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h @@ -13,106 +13,416 @@ #ifndef LLVM_EXECUTIONENGINE_JITLINK_JITLINKMEMORYMANAGER_H #define LLVM_EXECUTIONENGINE_JITLINK_JITLINKMEMORYMANAGER_H -#include "llvm/ADT/DenseMap.h" #include "llvm/ExecutionEngine/JITLink/JITLinkDylib.h" +#include "llvm/ExecutionEngine/JITLink/MemoryFlags.h" #include "llvm/ExecutionEngine/JITSymbol.h" +#include "llvm/Support/Allocator.h" #include "llvm/Support/Error.h" #include "llvm/Support/MSVCErrorWorkarounds.h" #include "llvm/Support/Memory.h" +#include "llvm/Support/RecyclingAllocator.h" #include #include +#include namespace llvm { namespace jitlink { +class Block; +class LinkGraph; +class Section; + /// Manages allocations of JIT memory. /// /// Instances of this class may be accessed concurrently from multiple threads /// and their implemetations should include any necessary synchronization. class JITLinkMemoryManager { public: - using ProtectionFlags = sys::Memory::ProtectionFlags; + /// Represents a call to a graph-memory-management support function in the + /// executor. + /// + /// Support functions are called as: + /// + /// auto *Result = + /// ((char*(*)(const void*, size_t))FnAddr)( + /// (const void*)CtxAddr, (size_t)CtxSize) + /// + /// A null result is interpreted as success. + /// + /// A non-null result is interpreted as a heap-allocated string containing + /// an error message to report to the allocator (the allocator's + /// executor-side implementation code is responsible for freeing the error + /// string). + struct AllocActionCall { + JITTargetAddress FnAddr = 0; + JITTargetAddress CtxAddr = 0; + JITTargetAddress CtxSize = 0; + }; + + /// A pair of AllocActionCalls, one to be run at finalization time, one to be + /// run at deallocation time. + /// + /// AllocActionCallPairs should be constructed for paired operations (e.g. + /// __register_ehframe and __deregister_ehframe for eh-frame registration). + /// See comments for AllocActions for execution ordering. + /// + /// For unpaired operations one or the other member can be left unused, as + /// AllocationActionCalls with an FnAddr of zero will be skipped. + struct AllocActionCallPair { + AllocActionCall Finalize; + AllocActionCall Dealloc; + }; + + /// A vector of allocation actions to be run for this allocation. + /// + /// Finalize allocations will be run in order at finalize time. Dealloc + /// actions will be run in reverse order at deallocation time. + using AllocActions = std::vector; + + /// Represents a finalized allocation. + /// + /// Finalized allocations must be passed to the + /// JITLinkMemoryManager:deallocate method prior to being destroyed. + /// + /// The interpretation of the Address associated with the finalized allocation + /// is up to the memory manager implementation. Common options are using the + /// base address of the allocation, or the address of a memory management + /// object that tracks the allocation. + class FinalizedAlloc { + friend class JITLinkMemoryManager; - class SegmentRequest { public: - SegmentRequest() = default; - SegmentRequest(uint64_t Alignment, size_t ContentSize, - uint64_t ZeroFillSize) - : Alignment(Alignment), ContentSize(ContentSize), - ZeroFillSize(ZeroFillSize) { - assert(isPowerOf2_32(Alignment) && "Alignment must be power of 2"); + static constexpr JITTargetAddress InvalidAddr = ~JITTargetAddress(0); + + FinalizedAlloc() = default; + explicit FinalizedAlloc(JITTargetAddress A) : A(A) { + assert(A != 0 && "Explicitly creating an invalid allocation?"); + } + FinalizedAlloc(const FinalizedAlloc &) = delete; + FinalizedAlloc(FinalizedAlloc &&Other) : A(Other.A) { + Other.A = InvalidAddr; + } + FinalizedAlloc &operator=(const FinalizedAlloc &) = delete; + FinalizedAlloc &operator=(FinalizedAlloc &&Other) { + assert(A == InvalidAddr && + "Cannot overwrite active finalized allocation"); + std::swap(A, Other.A); + return *this; + } + ~FinalizedAlloc() { + assert(A == InvalidAddr && "Finalized allocation was not deallocated"); + } + + /// FinalizedAllocs convert to false for default-constructed, and + /// true otherwise. Default-constructed allocs need not be deallocated. + explicit operator bool() const { return A != InvalidAddr; } + + /// Returns the address associated with this finalized allocation. + /// The allocation is unmodified. + JITTargetAddress getAddress() const { return A; } + + /// Returns the address associated with this finalized allocation and + /// resets this object to the default state. + /// This should only be used by allocators when deallocating memory. + JITTargetAddress release() { + JITTargetAddress Tmp = A; + A = InvalidAddr; + return Tmp; } - uint64_t getAlignment() const { return Alignment; } - size_t getContentSize() const { return ContentSize; } - uint64_t getZeroFillSize() const { return ZeroFillSize; } + private: - uint64_t Alignment = 0; - size_t ContentSize = 0; - uint64_t ZeroFillSize = 0; + JITTargetAddress A = InvalidAddr; }; - using SegmentsRequestMap = DenseMap; - - /// Represents an allocation created by the memory manager. + /// Represents an allocation which has not been finalized yet. /// - /// An allocation object is responsible for allocating and owning jit-linker - /// working and target memory, and for transfering from working to target - /// memory. + /// InFlightAllocs manage both executor memory allocations and working + /// memory allocations. /// - class Allocation { + /// On finalization, the InFlightAlloc should transfer the content of + /// working memory into executor memory, apply memory protections, and + /// run any finalization functions. + /// + /// Working memory should be kept alive at least until one of the following + /// happens: (1) the InFlightAlloc instance is destroyed, (2) the + /// InFlightAlloc is abandoned, (3) finalized target memory is destroyed. + /// + /// If abandon is called then working memory and executor memory should both + /// be freed. + class InFlightAlloc { public: - using FinalizeContinuation = std::function; - - virtual ~Allocation(); + using OnFinalizedFunction = unique_function)>; + using OnAbandonedFunction = unique_function; - /// Should return the address of linker working memory for the segment with - /// the given protection flags. - virtual MutableArrayRef getWorkingMemory(ProtectionFlags Seg) = 0; + virtual ~InFlightAlloc(); - /// Should return the final address in the target process where the segment - /// will reside. - virtual JITTargetAddress getTargetMemory(ProtectionFlags Seg) = 0; + /// Called prior to finalization if the allocation should be abandoned. + virtual void abandon(OnAbandonedFunction OnAbandoned) = 0; - /// Should transfer from working memory to target memory, and release - /// working memory. - virtual void finalizeAsync(FinalizeContinuation OnFinalize) = 0; + /// Called to transfer working memory to the target and apply finalization. + virtual void finalize(OnFinalizedFunction OnFinalized) = 0; - /// Calls finalizeAsync and waits for completion. - Error finalize() { - std::promise FinalizeResultP; + /// Synchronous convenience version of finalize. + Expected finalize() { + std::promise> FinalizeResultP; auto FinalizeResultF = FinalizeResultP.get_future(); - finalizeAsync( - [&](Error Err) { FinalizeResultP.set_value(std::move(Err)); }); + finalize([&](Expected Result) { + FinalizeResultP.set_value(std::move(Result)); + }); return FinalizeResultF.get(); } - - /// Should deallocate target memory. - virtual Error deallocate() = 0; }; + /// Typedef for the argument to be passed to OnAllocatedFunction. + using AllocResult = Expected>; + + /// Called when allocation has been completed. + using OnAllocatedFunction = unique_function; + + /// Called when deallocation has completed. + using OnDeallocatedFunction = unique_function; + virtual ~JITLinkMemoryManager(); - /// Create an Allocation object. + /// Start the allocation process. /// - /// The JD argument represents the target JITLinkDylib, and can be used by - /// JITLinkMemoryManager implementers to manage per-dylib allocation pools - /// (e.g. one pre-reserved address space slab per dylib to ensure that all - /// allocations for the dylib are within a certain range). The JD argument - /// may be null (representing an allocation not associated with any - /// JITDylib. + /// If the initial allocation is successful then the OnAllocated function will + /// be called with a std::unique_ptr value. If the assocation + /// is unsuccessful then the OnAllocated function will be called with an + /// Error. + virtual void allocate(const JITLinkDylib *JD, LinkGraph &G, + OnAllocatedFunction OnAllocated) = 0; + + /// Convenience function for blocking allocation. + AllocResult allocate(const JITLinkDylib *JD, LinkGraph &G) { + std::promise>> AllocResultP; + auto AllocResultF = AllocResultP.get_future(); + allocate(JD, G, [&](AllocResult Alloc) { + AllocResultP.set_value(std::move(Alloc)); + }); + return AllocResultF.get(); + } + + /// Deallocate a list of allocation objects. /// - /// The request argument describes the segment sizes and permisssions being - /// requested. - virtual Expected> - allocate(const JITLinkDylib *JD, const SegmentsRequestMap &Request) = 0; + /// Dealloc actions will be run in reverse order (from the end of the vector + /// to the start). + virtual void deallocate(std::vector Allocs, + OnDeallocatedFunction OnDeallocated) = 0; + + /// Convenience function for deallocation of a single alloc. + void deallocate(FinalizedAlloc Alloc, OnDeallocatedFunction OnDeallocated) { + std::vector Allocs; + Allocs.push_back(std::move(Alloc)); + deallocate(std::move(Allocs), std::move(OnDeallocated)); + } + + /// Convenience function for blocking deallocation. + Error deallocate(std::vector Allocs) { + std::promise DeallocResultP; + auto DeallocResultF = DeallocResultP.get_future(); + deallocate(std::move(Allocs), + [&](Error Err) { DeallocResultP.set_value(std::move(Err)); }); + return DeallocResultF.get(); + } + + /// Convenience function for blocking deallocation of a single alloc. + Error deallocate(FinalizedAlloc Alloc) { + std::vector Allocs; + Allocs.push_back(std::move(Alloc)); + return deallocate(std::move(Allocs)); + } +}; + +/// BasicLayout simplifies the implementation of JITLinkMemoryManagers. +/// +/// BasicLayout groups Sections into Segments based on their memory protection +/// and deallocation policies. JITLinkMemoryManagers can construct a BasicLayout +/// from a Graph, and then assign working memory and addresses to each of the +/// Segments. These addreses will be mapped back onto the Graph blocks in +/// the apply method. +class BasicLayout { +public: + /// The Alignment, ContentSize and ZeroFillSize of each segment will be + /// pre-filled from the Graph. Clients must set the Addr and WorkingMem fields + /// prior to calling apply. + // + // FIXME: The C++98 initializer is an attempt to work around compile failures + // due to http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1397. + // We should be able to switch this back to member initialization once that + // issue is fixed. + class Segment { + friend class BasicLayout; + + public: + Segment() + : ContentSize(0), ZeroFillSize(0), Addr(0), WorkingMem(nullptr), + NextWorkingMemOffset(0) {} + Align Alignment; + size_t ContentSize; + uint64_t ZeroFillSize; + JITTargetAddress Addr; + char *WorkingMem = nullptr; + + private: + size_t NextWorkingMemOffset; + std::vector ContentBlocks, ZeroFillBlocks; + }; + + /// A convenience class that further groups segments based on memory + /// deallocation policy. This allows clients to make two slab allocations: + /// one for all standard segments, and one for all finalize segments. + struct ContiguousPageBasedLayoutSizes { + uint64_t StandardSegs = 0; + uint64_t FinalizeSegs = 0; + + uint64_t total() const { return StandardSegs + FinalizeSegs; } + }; + +private: + using SegmentMap = AllocGroupSmallMap; + +public: + BasicLayout(LinkGraph &G); + + /// Return a reference to the graph this allocation was created from. + LinkGraph &getGraph() { return G; } + + /// Returns the total number of required to allocate all segments (with each + /// segment padded out to page size) for all standard segments, and all + /// finalize segments. + /// + /// This is a convenience function for the common case where the segments will + /// be allocated contiguously. + /// + /// This function will return an error if any segment has an alignment that + /// is higher than a page. + Expected + getContiguousPageBasedLayoutSizes(uint64_t PageSize); + + /// Returns an iterator over the segments of the layout. + iterator_range segments() { + return {Segments.begin(), Segments.end()}; + } + + /// Apply the layout to the graph. + Error apply(); + + /// Returns a reference to the AllocActions in the graph. + /// This convenience function saves callers from having to #include + /// LinkGraph.h if all they need are allocation actions. + JITLinkMemoryManager::AllocActions &graphAllocActions(); + +private: + LinkGraph &G; + SegmentMap Segments; +}; + +/// A utility class for making simple allocations using JITLinkMemoryManager. +/// +/// SimpleSegementAlloc takes a mapping of AllocGroups to Segments and uses +/// this to create a LinkGraph with one Section (containing one Block) per +/// Segment. Clients can obtain a pointer to the working memory and executor +/// address of that block using the Segment's AllocGroup. Once memory has been +/// populated, clients can call finalize to finalize the memory. +class SimpleSegmentAlloc { +public: + /// Describes a segment to be allocated. + struct Segment { + Segment() = default; + Segment(size_t ContentSize, Align ContentAlign) + : ContentSize(ContentSize), ContentAlign(ContentAlign) {} + + size_t ContentSize = 0; + Align ContentAlign; + }; + + /// Describes the segment working memory and executor address. + struct SegmentInfo { + JITTargetAddress Addr = 0; + MutableArrayRef WorkingMem; + }; + + using SegmentMap = AllocGroupSmallMap; + + using OnCreatedFunction = unique_function)>; + + using OnFinalizedFunction = + JITLinkMemoryManager::InFlightAlloc::OnFinalizedFunction; + + static void Create(JITLinkMemoryManager &MemMgr, const JITLinkDylib *JD, + SegmentMap Segments, OnCreatedFunction OnCreated); + + static Expected Create(JITLinkMemoryManager &MemMgr, + const JITLinkDylib *JD, + SegmentMap Segments); + + SimpleSegmentAlloc(SimpleSegmentAlloc &&); + SimpleSegmentAlloc &operator=(SimpleSegmentAlloc &&); + ~SimpleSegmentAlloc(); + + /// Returns the SegmentInfo for the given group. + SegmentInfo getSegInfo(AllocGroup AG); + + /// Finalize all groups (async version). + void finalize(OnFinalizedFunction OnFinalized) { + Alloc->finalize(std::move(OnFinalized)); + } + + /// Finalize all groups. + Expected finalize() { + return Alloc->finalize(); + } + +private: + SimpleSegmentAlloc( + std::unique_ptr G, AllocGroupSmallMap ContentBlocks, + std::unique_ptr Alloc); + + std::unique_ptr G; + AllocGroupSmallMap ContentBlocks; + std::unique_ptr Alloc; }; /// A JITLinkMemoryManager that allocates in-process memory. class InProcessMemoryManager : public JITLinkMemoryManager { public: - Expected> - allocate(const JITLinkDylib *JD, const SegmentsRequestMap &Request) override; + class IPInFlightAlloc; + + /// Attempts to auto-detect the host page size. + static Expected> Create(); + + /// Create an instance using the given page size. + InProcessMemoryManager(uint64_t PageSize) : PageSize(PageSize) {} + + void allocate(const JITLinkDylib *JD, LinkGraph &G, + OnAllocatedFunction OnAllocated) override; + + // Use overloads from base class. + using JITLinkMemoryManager::allocate; + + void deallocate(std::vector Alloc, + OnDeallocatedFunction OnDeallocated) override; + + // Use overloads from base class. + using JITLinkMemoryManager::deallocate; + +private: + // FIXME: Use an in-place array instead of a vector for DeallocActions. + // There shouldn't need to be a heap alloc for this. + struct FinalizedAllocInfo { + sys::MemoryBlock StandardSegments; + std::vector DeallocActions; + }; + + FinalizedAlloc + createFinalizedAlloc(sys::MemoryBlock StandardSegments, + std::vector DeallocActions); + + uint64_t PageSize; + std::mutex FinalizedAllocsMutex; + RecyclingAllocator FinalizedAllocInfos; }; } // end namespace jitlink diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/MachO_arm64.h b/llvm/include/llvm/ExecutionEngine/JITLink/MachO_arm64.h index ecbc93e1467d..aee14c0d1fe5 100644 --- a/llvm/include/llvm/ExecutionEngine/JITLink/MachO_arm64.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/MachO_arm64.h @@ -29,6 +29,8 @@ enum MachOARM64RelocationKind : Edge::Kind { PageOffset12, GOTPage21, GOTPageOffset12, + TLVPage21, + TLVPageOffset12, PointerToGOT, PairedAddend, LDRLiteral19, diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/MemoryFlags.h b/llvm/include/llvm/ExecutionEngine/JITLink/MemoryFlags.h new file mode 100644 index 000000000000..8fdce93ebc56 --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/JITLink/MemoryFlags.h @@ -0,0 +1,225 @@ +//===-------- MemoryFlags.h - Memory allocation flags -----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines types and operations related to memory protection and allocation +// lifetimes. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_JITLINK_MEMORYFLAGS_H +#define LLVM_EXECUTIONENGINE_JITLINK_MEMORYFLAGS_H + +#include "llvm/ADT/BitmaskEnum.h" +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/Support/Memory.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +namespace jitlink { + +/// Describes Read/Write/Exec permissions for memory. +enum class MemProt { + None = 0, + Read = 1U << 0, + Write = 1U << 1, + Exec = 1U << 2, + LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ Exec) +}; + +/// Print a MemProt as an RWX triple. +raw_ostream &operator<<(raw_ostream &OS, MemProt MP); + +/// Convert a MemProt value to a corresponding sys::Memory::ProtectionFlags +/// value. +inline sys::Memory::ProtectionFlags toSysMemoryProtectionFlags(MemProt MP) { + std::underlying_type_t PF = 0; + if ((MP & MemProt::Read) != MemProt::None) + PF |= sys::Memory::MF_READ; + if ((MP & MemProt::Write) != MemProt::None) + PF |= sys::Memory::MF_WRITE; + if ((MP & MemProt::Exec) != MemProt::None) + PF |= sys::Memory::MF_EXEC; + return static_cast(PF); +} + +/// Convert a sys::Memory::ProtectionFlags value to a corresponding MemProt +/// value. +inline MemProt fromSysMemoryProtectionFlags(sys::Memory::ProtectionFlags PF) { + MemProt MP = MemProt::None; + if (PF & sys::Memory::MF_READ) + MP |= MemProt::Read; + if (PF & sys::Memory::MF_WRITE) + MP |= MemProt::Write; + if (PF & sys::Memory::MF_EXEC) + MP |= MemProt::None; + return MP; +} + +/// Describes a memory deallocation policy for memory to be allocated by a +/// JITLinkMemoryManager. +/// +/// All memory allocated by a call to JITLinkMemoryManager::allocate should be +/// deallocated if a call is made to +/// JITLinkMemoryManager::InFlightAllocation::abandon. The policies below apply +/// to finalized allocations. +enum class MemDeallocPolicy { + /// Standard memory should be deallocated when the deallocate method is called + /// for the finalized allocation. + Standard, + + /// Finalize memory should be overwritten and then deallocated after all + /// finalization functions have been run. + Finalize +}; + +/// Print a MemDeallocPolicy. +raw_ostream &operator<<(raw_ostream &OS, MemDeallocPolicy MDP); + +/// A pair of memory protections and allocation policies. +/// +/// Optimized for use as a small map key. +class AllocGroup { + friend struct llvm::DenseMapInfo; + + using underlying_type = uint8_t; + static constexpr unsigned BitsForProt = 3; + static constexpr unsigned BitsForDeallocPolicy = 1; + static constexpr unsigned MaxIdentifiers = + 1U << (BitsForProt + BitsForDeallocPolicy); + +public: + static constexpr unsigned NumGroups = MaxIdentifiers; + + /// Create a default AllocGroup. No memory protections, standard + /// deallocation policy. + AllocGroup() = default; + + /// Create an AllocGroup from a MemProt only -- uses + /// MemoryDeallocationPolicy::Standard. + AllocGroup(MemProt MP) : Id(static_cast(MP)) {} + + /// Create an AllocGroup from a MemProt and a MemoryDeallocationPolicy. + AllocGroup(MemProt MP, MemDeallocPolicy MDP) + : Id(static_cast(MP) | + (static_cast(MDP) << BitsForProt)) {} + + /// Returns the MemProt for this group. + MemProt getMemProt() const { + return static_cast(Id & ((1U << BitsForProt) - 1)); + } + + /// Returns the MemoryDeallocationPolicy for this group. + MemDeallocPolicy getMemDeallocPolicy() const { + return static_cast(Id >> BitsForProt); + } + + friend bool operator==(const AllocGroup &LHS, const AllocGroup &RHS) { + return LHS.Id == RHS.Id; + } + + friend bool operator!=(const AllocGroup &LHS, const AllocGroup &RHS) { + return !(LHS == RHS); + } + + friend bool operator<(const AllocGroup &LHS, const AllocGroup &RHS) { + return LHS.Id < RHS.Id; + } + +private: + AllocGroup(underlying_type RawId) : Id(RawId) {} + underlying_type Id = 0; +}; + +/// A specialized small-map for AllocGroups. +/// +/// Iteration order is guaranteed to match key ordering. +template class AllocGroupSmallMap { +private: + using ElemT = std::pair; + using VectorTy = SmallVector; + + static bool compareKey(const ElemT &E, const AllocGroup &G) { + return E.first < G; + } + +public: + using iterator = typename VectorTy::iterator; + + AllocGroupSmallMap() = default; + AllocGroupSmallMap(std::initializer_list> Inits) { + Elems.reserve(Inits.size()); + for (const auto &E : Inits) + Elems.push_back(E); + llvm::sort(Elems, [](const ElemT &LHS, const ElemT &RHS) { + return LHS.first < RHS.first; + }); + } + + iterator begin() { return Elems.begin(); } + iterator end() { return Elems.end(); } + iterator find(AllocGroup G) { + auto I = lower_bound(Elems, G, compareKey); + return (I->first == G) ? I : end(); + } + + bool empty() const { return Elems.empty(); } + size_t size() const { return Elems.size(); } + + T &operator[](AllocGroup G) { + auto I = lower_bound(Elems, G, compareKey); + if (I == Elems.end() || I->first != G) + I = Elems.insert(I, std::make_pair(G, T())); + return I->second; + } + +private: + VectorTy Elems; +}; + +/// Print an AllocGroup. +raw_ostream &operator<<(raw_ostream &OS, AllocGroup AG); + +} // end namespace jitlink + +template <> struct DenseMapInfo { + static inline jitlink::MemProt getEmptyKey() { + return jitlink::MemProt(~uint8_t(0)); + } + static inline jitlink::MemProt getTombstoneKey() { + return jitlink::MemProt(~uint8_t(0) - 1); + } + static unsigned getHashValue(const jitlink::MemProt &Val) { + using UT = std::underlying_type_t; + return DenseMapInfo::getHashValue(static_cast(Val)); + } + static bool isEqual(const jitlink::MemProt &LHS, + const jitlink::MemProt &RHS) { + return LHS == RHS; + } +}; + +template <> struct DenseMapInfo { + static inline jitlink::AllocGroup getEmptyKey() { + return jitlink::AllocGroup(~uint8_t(0)); + } + static inline jitlink::AllocGroup getTombstoneKey() { + return jitlink::AllocGroup(~uint8_t(0) - 1); + } + static unsigned getHashValue(const jitlink::AllocGroup &Val) { + return DenseMapInfo::getHashValue( + Val.Id); + } + static bool isEqual(const jitlink::AllocGroup &LHS, + const jitlink::AllocGroup &RHS) { + return LHS == RHS; + } +}; + +} // end namespace llvm + +#endif // LLVM_EXECUTIONENGINE_JITLINK_MEMORYFLAGS_H diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/TableManager.h b/llvm/include/llvm/ExecutionEngine/JITLink/TableManager.h new file mode 100644 index 000000000000..c20f62d515ec --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/JITLink/TableManager.h @@ -0,0 +1,63 @@ +//===---------------------- TableManager.h ----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Fix edge for edge that needs an entry to reference the target symbol +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_JITLINK_TABLEMANAGER_H +#define LLVM_EXECUTIONENGINE_JITLINK_TABLEMANAGER_H + +#include "llvm/ExecutionEngine/JITLink/JITLink.h" +#include "llvm/Support/Debug.h" + +namespace llvm { +namespace jitlink { + +/// A CRTP base for tables that are built on demand, e.g. Global Offset Tables +/// and Procedure Linkage Tables. +/// The getEntyrForTarget function returns the table entry corresponding to the +/// given target, calling down to the implementation class to build an entry if +/// one does not already exist. +template class TableManager { +public: + /// Return the constructed entry + /// + /// Use parameter G to construct the entry for target symbol + Symbol &getEntryForTarget(LinkGraph &G, Symbol &Target) { + assert(Target.hasName() && "Edge cannot point to anonymous target"); + + auto EntryI = Entries.find(Target.getName()); + + // Build the entry if it doesn't exist. + if (EntryI == Entries.end()) { + auto &Entry = impl().createEntry(G, Target); + DEBUG_WITH_TYPE("jitlink", { + dbgs() << " Created" << impl().getSectionName() << "entry for " + << Target.getName() << ": " << Entry << "\n"; + }); + EntryI = Entries.insert(std::make_pair(Target.getName(), &Entry)).first; + } + + assert(EntryI != Entries.end() && "Could not get entry symbol"); + DEBUG_WITH_TYPE("jitlink", { + dbgs() << " Using " << impl().getSectionName() << " entry " + << *EntryI->second << "\n"; + }); + return *EntryI->second; + } + +private: + TableManagerImplT &impl() { return static_cast(*this); } + DenseMap Entries; +}; + +} // namespace jitlink +} // namespace llvm + +#endif diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h b/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h new file mode 100644 index 000000000000..994ce783b058 --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h @@ -0,0 +1,38 @@ +//=== aarch64.h - Generic JITLink aarch64 edge kinds, utilities -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Generic utilities for graphs representing aarch64 objects. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_JITLINK_AARCH64_H +#define LLVM_EXECUTIONENGINE_JITLINK_AARCH64_H + +#include "llvm/ExecutionEngine/JITLink/JITLink.h" + +namespace llvm { +namespace jitlink { +namespace aarch64 { + +/// Represets aarch64 fixups +enum EdgeKind_aarch64 : Edge::Kind { + + /// Set a CALL immediate field to bits [27:2] of X = Target - Fixup + Addend + R_AARCH64_CALL26 = Edge::FirstRelocation, + +}; + +/// Returns a string name for the given aarch64 edge. For debugging purposes +/// only +const char *getEdgeKindName(Edge::Kind K); + +} // namespace aarch64 +} // namespace jitlink +} // namespace llvm + +#endif // LLVM_EXECUTIONENGINE_JITLINK_AARCH64_H diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/riscv.h b/llvm/include/llvm/ExecutionEngine/JITLink/riscv.h index a4509f3888a4..b8d08d88c1c9 100644 --- a/llvm/include/llvm/ExecutionEngine/JITLink/riscv.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/riscv.h @@ -70,7 +70,19 @@ enum EdgeKind_riscv : Edge::Kind { /// /// Fixup expression: /// Fixup <- (Target - Fixup + Addend) - R_RISCV_CALL + R_RISCV_CALL, + + /// PC relative GOT offset + /// + /// Fixup expression: + /// Fixup <- (GOT - Fixup + Addend) >> 12 + R_RISCV_GOT_HI20, + + /// PC relative call by PLT + /// + /// Fixup expression: + /// Fixup <- (Target - Fixup + Addend) + R_RISCV_CALL_PLT }; diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/x86_64.h b/llvm/include/llvm/ExecutionEngine/JITLink/x86_64.h index 006d983537e9..3130ea381534 100644 --- a/llvm/include/llvm/ExecutionEngine/JITLink/x86_64.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/x86_64.h @@ -14,6 +14,7 @@ #define LLVM_EXECUTIONENGINE_JITLINK_X86_64_H #include "llvm/ExecutionEngine/JITLink/JITLink.h" +#include "llvm/ExecutionEngine/JITLink/TableManager.h" #include @@ -42,6 +43,16 @@ enum EdgeKind_x86_64 : Edge::Kind { /// Pointer32, + /// A signed 32-bit pointer value relocation + /// + /// Fixup expression: + /// Fixup <- Target + Addend : int32 + /// + /// Errors: + /// - The target must reside in the signed 32-bits([-2**31, 2**32 - 1]) of + /// the address space, otherwise an out-of-range error will be returned. + Pointer32Signed, + /// A 64-bit delta. /// /// Delta from the fixup to the target. @@ -85,6 +96,18 @@ enum EdgeKind_x86_64 : Edge::Kind { /// an out-of-range error will be returned. NegDelta32, + /// A 64-bit GOT delta. + /// + /// Delta from the global offset table to the target + /// + /// Fixup expression: + /// Fixup <- Target - GOTSymbol + Addend : int64 + /// + /// Errors: + /// - *ASSERTION* Failure to a null pointer GOTSymbol, which the GOT section + /// symbol was not been defined. + Delta64FromGOT, + /// A 32-bit PC-relative branch. /// /// Represents a PC-relative call or branch to a target. This can be used to @@ -120,7 +143,7 @@ enum EdgeKind_x86_64 : Edge::Kind { /// This edge kind has the same fixup expression as BranchPCRel32, but further /// identifies the call/branch as being to a pointer jump stub. For edges of /// this kind the jump stub should not be bypassed (use - /// BranchPCRel32ToPtrJumpStubRelaxable for that), but the pointer location + /// BranchPCRel32ToPtrJumpStubBypassable for that), but the pointer location /// target may be recorded to allow manipulation at runtime. /// /// Fixup expression: @@ -136,7 +159,8 @@ enum EdgeKind_x86_64 : Edge::Kind { /// /// The edge kind has the same fixup expression as BranchPCRel32ToPtrJumpStub, /// but identifies the call/branch as being to a pointer jump stub that may be - /// bypassed if the ultimate target is within range of the fixup location. + /// bypassed with a direct jump to the ultimate target if the ultimate target + /// is within range of the fixup location. /// /// Fixup expression: /// Fixup <- Target - Fixup + Addend - 4: int32 @@ -145,7 +169,7 @@ enum EdgeKind_x86_64 : Edge::Kind { /// - The result of the fixup expression must fit into an int32, otherwise /// an out-of-range error will be returned. /// - BranchPCRel32ToPtrJumpStubRelaxable, + BranchPCRel32ToPtrJumpStubBypassable, /// A GOT entry getter/constructor, transformed to Delta32 pointing at the GOT /// entry for the original target. @@ -167,7 +191,62 @@ enum EdgeKind_x86_64 : Edge::Kind { /// RequestGOTAndTransformToDelta32, - /// A PC-relative reference to a GOT entry, relaxable if GOT entry target + /// A GOT entry getter/constructor, transformed to Delta64 pointing at the GOT + /// entry for the original target. + /// + /// Indicates that this edge should be transformed into a Delta64 targeting + /// the GOT entry for the edge's current target, maintaining the same addend. + /// A GOT entry for the target should be created if one does not already + /// exist. + /// + /// Edges of this kind are usually handled by a GOT builder pass inserted by + /// default. + /// + /// Fixup expression: + /// NONE + /// + /// Errors: + /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup + /// phase will result in an assert/unreachable during the fixup phase. + /// + RequestGOTAndTransformToDelta64, + + /// A GOT entry offset within GOT getter/constructor, transformed to + /// Delta64FromGOT + /// pointing at the GOT entry for the original target + /// + /// Indicates that this edge should be transformed into a Delta64FromGOT + /// targeting + /// the GOT entry for the edge's current target, maintaining the same addend. + /// A GOT entry for the target should be created if one does not already + /// exist. + /// + /// Edges of this kind are usually handled by a GOT builder pass inserted by + /// default + /// + /// Fixup expression: + /// NONE + /// + /// Errors: + /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup + /// phase will result in an assert/unreachable during the fixup phase + RequestGOTAndTransformToDelta64FromGOT, + + /// A PC-relative load of a GOT entry, relaxable if GOT entry target is + /// in-range of the fixup + /// + /// TODO: Explain the optimization + /// + /// Fixup expression + /// Fixup <- Target - (Fixup + 4) + Addend : int32 + /// + /// Errors: + /// - The result of the fixup expression must fit into an int32, otherwise + /// an out-of-range error will be returned. + // + PCRel32GOTLoadRelaxable, + + /// A PC-relative REX load of a GOT entry, relaxable if GOT entry target /// is in-range of the fixup. /// /// If the GOT entry target is in-range of the fixup then the load from the @@ -180,17 +259,39 @@ enum EdgeKind_x86_64 : Edge::Kind { /// - The result of the fixup expression must fit into an int32, otherwise /// an out-of-range error will be returned. /// - PCRel32GOTLoadRelaxable, + PCRel32GOTLoadREXRelaxable, - /// A GOT entry getter/constructor, transformed to PCRel32ToGOTLoadRelaxable - /// pointing at the GOT entry for the original target. + /// A GOT entry getter/constructor, transformed to + /// PCRel32ToGOTLoadREXRelaxable pointing at the GOT entry for the original + /// target. /// - /// Indicates that this edge should be transformed into a - /// PC32ToGOTLoadRelaxable targeting the GOT entry for the edge's current - /// target, maintaining the same addend. A GOT entry for the target should be - /// created if one does not already exist. + /// Indicates that this edge should be lowered to a PC32ToGOTLoadREXRelaxable + /// targeting the GOT entry for the edge's current target, maintaining the + /// same addend. A GOT entry for the target should be created if one does not + /// already exist. /// - /// Edges of this kind are usually handled by a GOT builder pass inserted by + /// Edges of this kind are usually lowered by a GOT builder pass inserted by + /// default. + /// + /// Fixup expression: + /// NONE + /// + /// Errors: + /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup + /// phase will result in an assert/unreachable during the fixup phase. + /// + RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable, + + /// A GOT entry getter/constructor, transformed to + /// PCRel32ToGOTLoadRelaxable pointing at the GOT entry for the original + /// target. + /// + /// Indicates that this edge should be lowered to a PC32ToGOTLoadRelaxable + /// targeting the GOT entry for the edge's current target, maintaining the + /// same addend. A GOT entry for the target should be created if one does not + /// already exist. + /// + /// Edges of this kind are usually lowered by a GOT builder pass inserted by /// default. /// /// Fixup expression: @@ -202,10 +303,10 @@ enum EdgeKind_x86_64 : Edge::Kind { /// RequestGOTAndTransformToPCRel32GOTLoadRelaxable, - /// A PC-relative reference to a Thread Local Variable Pointer (TLVP) entry, + /// A PC-relative REX load of a Thread Local Variable Pointer (TLVP) entry, /// relaxable if the TLVP entry target is in-range of the fixup. /// - /// If the TLVP entry target is in-range of the fixup then the load frmo the + /// If the TLVP entry target is in-range of the fixup then the load from the /// TLVP may be replaced with a direct memory address calculation. /// /// The target of this edge must be a thread local variable entry of the form @@ -222,15 +323,18 @@ enum EdgeKind_x86_64 : Edge::Kind { /// - The target must be either external, or a TLV entry of the required /// form, otherwise a malformed TLV entry error will be returned. /// - PCRel32TLVPLoadRelaxable, + PCRel32TLVPLoadREXRelaxable, + + /// TODO: Explain the generic edge kind + RequestTLSDescInGOTAndTransformToDelta32, /// A TLVP entry getter/constructor, transformed to - /// Delta32ToTLVPLoadRelaxable. + /// Delta32ToTLVPLoadREXRelaxable. /// /// Indicates that this edge should be transformed into a - /// Delta32ToTLVPLoadRelaxable targeting the TLVP entry for the edge's current - /// target. A TLVP entry for the target should be created if one does not - /// already exist. + /// Delta32ToTLVPLoadREXRelaxable targeting the TLVP entry for the edge's + /// current target. A TLVP entry for the target should be created if one does + /// not already exist. /// /// Fixup expression: /// NONE @@ -239,7 +343,7 @@ enum EdgeKind_x86_64 : Edge::Kind { /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup /// phase will result in an assert/unreachable during the fixup phase. /// - RequestTLVPAndTransformToPCRel32TLVPLoadRelaxable + RequestTLVPAndTransformToPCRel32TLVPLoadREXRelaxable }; /// Returns a string name for the given x86-64 edge. For debugging purposes @@ -258,7 +362,8 @@ inline bool isInRangeForImmS32(int64_t Value) { } /// Apply fixup expression for edge to block content. -inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E) { +inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E, + const Symbol *GOTSymbol) { using namespace support; char *BlockWorkingMem = B.getAlreadyMutableContent().data(); @@ -281,12 +386,21 @@ inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E) { return makeTargetOutOfRangeError(G, B, E); break; } + case Pointer32Signed: { + int64_t Value = E.getTarget().getAddress() + E.getAddend(); + if (LLVM_LIKELY(isInRangeForImmS32(Value))) + *(little32_t *)FixupPtr = Value; + else + return makeTargetOutOfRangeError(G, B, E); + break; + } case BranchPCRel32: case BranchPCRel32ToPtrJumpStub: - case BranchPCRel32ToPtrJumpStubRelaxable: + case BranchPCRel32ToPtrJumpStubBypassable: case PCRel32GOTLoadRelaxable: - case PCRel32TLVPLoadRelaxable: { + case PCRel32GOTLoadREXRelaxable: + case PCRel32TLVPLoadREXRelaxable: { int64_t Value = E.getTarget().getAddress() - (FixupAddress + 4) + E.getAddend(); if (LLVM_LIKELY(isInRangeForImmS32(Value))) @@ -325,6 +439,13 @@ inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E) { return makeTargetOutOfRangeError(G, B, E); break; } + case Delta64FromGOT: { + assert(GOTSymbol && "No GOT section symbol"); + int64_t Value = + E.getTarget().getAddress() - GOTSymbol->getAddress() + E.getAddend(); + *(little64_t *)FixupPtr = Value; + break; + } default: { // If you hit this you should check that *constructor and other non-fixup @@ -395,6 +516,114 @@ inline Symbol &createAnonymousPointerJumpStub(LinkGraph &G, false); } +/// Global Offset Table Builder. +class GOTTableManager : public TableManager { +public: + static StringRef getSectionName() { return "$__GOT"; } + + bool visitEdge(LinkGraph &G, Block *B, Edge &E) { + Edge::Kind KindToSet = Edge::Invalid; + switch (E.getKind()) { + case x86_64::Delta64FromGOT: { + // we need to make sure that the GOT section exists, but don't otherwise + // need to fix up this edge + getGOTSection(G); + return false; + } + case x86_64::RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable: + KindToSet = x86_64::PCRel32GOTLoadREXRelaxable; + break; + case x86_64::RequestGOTAndTransformToPCRel32GOTLoadRelaxable: + KindToSet = x86_64::PCRel32GOTLoadRelaxable; + break; + case x86_64::RequestGOTAndTransformToDelta64: + KindToSet = x86_64::Delta64; + break; + case x86_64::RequestGOTAndTransformToDelta64FromGOT: + KindToSet = x86_64::Delta64FromGOT; + break; + case x86_64::RequestGOTAndTransformToDelta32: + KindToSet = x86_64::Delta32; + break; + default: + return false; + } + assert(KindToSet != Edge::Invalid && + "Fell through switch, but no new kind to set"); + DEBUG_WITH_TYPE("jitlink", { + dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at " + << formatv("{0:x}", B->getFixupAddress(E)) << " (" + << formatv("{0:x}", B->getAddress()) << " + " + << formatv("{0:x}", E.getOffset()) << ")\n"; + }); + E.setKind(KindToSet); + E.setTarget(getEntryForTarget(G, E.getTarget())); + return true; + } + + Symbol &createEntry(LinkGraph &G, Symbol &Target) { + return createAnonymousPointer(G, getGOTSection(G), &Target); + } + +private: + Section &getGOTSection(LinkGraph &G) { + if (!GOTSection) + GOTSection = &G.createSection(getSectionName(), MemProt::Read); + return *GOTSection; + } + + Section *GOTSection = nullptr; +}; + +/// Procedure Linkage Table Builder. +class PLTTableManager : public TableManager { +public: + PLTTableManager(GOTTableManager &GOT) : GOT(GOT) {} + + static StringRef getSectionName() { return "$__STUBS"; } + + bool visitEdge(LinkGraph &G, Block *B, Edge &E) { + if (E.getKind() == x86_64::BranchPCRel32 && !E.getTarget().isDefined()) { + DEBUG_WITH_TYPE("jitlink", { + dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at " + << formatv("{0:x}", B->getFixupAddress(E)) << " (" + << formatv("{0:x}", B->getAddress()) << " + " + << formatv("{0:x}", E.getOffset()) << ")\n"; + }); + // Set the edge kind to Branch32ToPtrJumpStubBypassable to enable it to + // be optimized when the target is in-range. + E.setKind(x86_64::BranchPCRel32ToPtrJumpStubBypassable); + E.setTarget(getEntryForTarget(G, E.getTarget())); + return true; + } + return false; + } + + Symbol &createEntry(LinkGraph &G, Symbol &Target) { + return createAnonymousPointerJumpStub(G, getStubsSection(G), + GOT.getEntryForTarget(G, Target)); + } + +public: + Section &getStubsSection(LinkGraph &G) { + if (!PLTSection) + PLTSection = + &G.createSection(getSectionName(), MemProt::Read | MemProt::Exec); + return *PLTSection; + } + + GOTTableManager &GOT; + Section *PLTSection = nullptr; +}; + +/// Optimize the GOT and Stub relocations if the edge target address is in range +/// 1. PCRel32GOTLoadRelaxable. For this edge kind, if the target is in range, +/// then replace GOT load with lea +/// 2. BranchPCRel32ToPtrJumpStubRelaxable. For this edge kind, if the target is +/// in range, replace a indirect jump by plt stub with a direct jump to the +/// target +Error optimizeGOTAndStubAccesses(LinkGraph &G); + } // namespace x86_64 } // end namespace jitlink } // end namespace llvm diff --git a/llvm/include/llvm/ExecutionEngine/MCJIT.h b/llvm/include/llvm/ExecutionEngine/MCJIT.h index 8253bf98963b..adce98f380c5 100644 --- a/llvm/include/llvm/ExecutionEngine/MCJIT.h +++ b/llvm/include/llvm/ExecutionEngine/MCJIT.h @@ -26,6 +26,9 @@ namespace { // delete it all as dead code, even with whole program optimization, // yet is effectively a NO-OP. As the compiler isn't smart enough // to know that getenv() never returns -1, this will do the job. + // This is so that globals in the translation units where these functions + // are defined are forced to be initialized, populating various + // registries. if (std::getenv("bar") != (char*) -1) return; diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Core.h b/llvm/include/llvm/ExecutionEngine/Orc/Core.h index e832d8d57dfa..5cac65b49a05 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/Core.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/Core.h @@ -21,7 +21,7 @@ #include "llvm/ExecutionEngine/JITSymbol.h" #include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h" #include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h" -#include "llvm/ExecutionEngine/OrcV1Deprecation.h" +#include "llvm/ExecutionEngine/Orc/TaskDispatch.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ExtensibleRTTI.h" @@ -434,13 +434,16 @@ class SymbolsNotFound : public ErrorInfo { public: static char ID; - SymbolsNotFound(SymbolNameSet Symbols); - SymbolsNotFound(SymbolNameVector Symbols); + SymbolsNotFound(std::shared_ptr SSP, SymbolNameSet Symbols); + SymbolsNotFound(std::shared_ptr SSP, + SymbolNameVector Symbols); std::error_code convertToErrorCode() const override; void log(raw_ostream &OS) const override; + std::shared_ptr getSymbolStringPool() { return SSP; } const SymbolNameVector &getSymbols() const { return Symbols; } private: + std::shared_ptr SSP; SymbolNameVector Symbols; }; @@ -449,12 +452,15 @@ class SymbolsCouldNotBeRemoved : public ErrorInfo { public: static char ID; - SymbolsCouldNotBeRemoved(SymbolNameSet Symbols); + SymbolsCouldNotBeRemoved(std::shared_ptr SSP, + SymbolNameSet Symbols); std::error_code convertToErrorCode() const override; void log(raw_ostream &OS) const override; + std::shared_ptr getSymbolStringPool() { return SSP; } const SymbolNameSet &getSymbols() const { return Symbols; } private: + std::shared_ptr SSP; SymbolNameSet Symbols; }; @@ -466,13 +472,17 @@ class MissingSymbolDefinitions : public ErrorInfo { public: static char ID; - MissingSymbolDefinitions(std::string ModuleName, SymbolNameVector Symbols) - : ModuleName(std::move(ModuleName)), Symbols(std::move(Symbols)) {} + MissingSymbolDefinitions(std::shared_ptr SSP, + std::string ModuleName, SymbolNameVector Symbols) + : SSP(std::move(SSP)), ModuleName(std::move(ModuleName)), + Symbols(std::move(Symbols)) {} std::error_code convertToErrorCode() const override; void log(raw_ostream &OS) const override; + std::shared_ptr getSymbolStringPool() { return SSP; } const std::string &getModuleName() const { return ModuleName; } const SymbolNameVector &getSymbols() const { return Symbols; } private: + std::shared_ptr SSP; std::string ModuleName; SymbolNameVector Symbols; }; @@ -485,13 +495,17 @@ class UnexpectedSymbolDefinitions : public ErrorInfo SSP, + std::string ModuleName, SymbolNameVector Symbols) + : SSP(std::move(SSP)), ModuleName(std::move(ModuleName)), + Symbols(std::move(Symbols)) {} std::error_code convertToErrorCode() const override; void log(raw_ostream &OS) const override; + std::shared_ptr getSymbolStringPool() { return SSP; } const std::string &getModuleName() const { return ModuleName; } const SymbolNameVector &getSymbols() const { return Symbols; } private: + std::shared_ptr SSP; std::string ModuleName; SymbolNameVector Symbols; }; @@ -1241,21 +1255,6 @@ public: const DenseMap &InitSyms); }; -/// Represents an abstract task for ORC to run. -class Task : public RTTIExtends { -public: - static char ID; - - /// Description of the task to be performed. Used for logging. - virtual void printDescription(raw_ostream &OS) = 0; - - /// Run the task. - virtual void run() = 0; - -private: - void anchor() override; -}; - /// A materialization task. class MaterializationTask : public RTTIExtends { public: @@ -1285,13 +1284,16 @@ public: /// For reporting errors. using ErrorReporter = std::function; + /// Send a result to the remote. + using SendResultFunction = unique_function; + /// For dispatching ORC tasks (typically materialization tasks). using DispatchTaskFunction = unique_function T)>; /// An asynchronous wrapper-function callable from the executor via /// jit-dispatch. using JITDispatchHandlerFunction = unique_function; /// A map associating tag names with asynchronous wrapper function @@ -1303,13 +1305,19 @@ public: /// object. ExecutionSession(std::unique_ptr EPC); - /// End the session. Closes all JITDylibs. + /// End the session. Closes all JITDylibs and disconnects from the + /// executor. Error endSession(); /// Get the ExecutorProcessControl object associated with this /// ExecutionSession. ExecutorProcessControl &getExecutorProcessControl() { return *EPC; } + /// Get the SymbolStringPool for this instance. + std::shared_ptr getSymbolStringPool() { + return EPC->getSymbolStringPool(); + } + /// Add a symbol name to the SymbolStringPool and return a pointer to it. SymbolStringPtr intern(StringRef SymName) { return EPC->intern(SymName); } @@ -1462,10 +1470,9 @@ public: /// \endcode{.cpp} /// /// The given OnComplete function will be called to return the result. - void callWrapperAsync(ExecutorProcessControl::SendResultFunction OnComplete, - JITTargetAddress WrapperFnAddr, - ArrayRef ArgBuffer) { - EPC->callWrapperAsync(std::move(OnComplete), WrapperFnAddr, ArgBuffer); + template + void callWrapperAsync(ArgTs &&... Args) { + EPC->callWrapperAsync(std::forward(Args)...); } /// Run a wrapper function in the executor. The wrapper function should be @@ -1474,30 +1481,18 @@ public: /// \code{.cpp} /// CWrapperFunctionResult fn(uint8_t *Data, uint64_t Size); /// \endcode{.cpp} - shared::WrapperFunctionResult callWrapper(JITTargetAddress WrapperFnAddr, + shared::WrapperFunctionResult callWrapper(ExecutorAddr WrapperFnAddr, ArrayRef ArgBuffer) { - std::promise RP; - auto RF = RP.get_future(); - callWrapperAsync( - [&](shared::WrapperFunctionResult R) { RP.set_value(std::move(R)); }, - WrapperFnAddr, ArgBuffer); - return RF.get(); + return EPC->callWrapper(WrapperFnAddr, ArgBuffer); } /// Run a wrapper function using SPS to serialize the arguments and /// deserialize the results. template - void callSPSWrapperAsync(SendResultT &&SendResult, - JITTargetAddress WrapperFnAddr, + void callSPSWrapperAsync(ExecutorAddr WrapperFnAddr, SendResultT &&SendResult, const ArgTs &...Args) { - shared::WrapperFunction::callAsync( - [this, - WrapperFnAddr](ExecutorProcessControl::SendResultFunction SendResult, - const char *ArgData, size_t ArgSize) { - callWrapperAsync(std::move(SendResult), WrapperFnAddr, - ArrayRef(ArgData, ArgSize)); - }, - std::move(SendResult), Args...); + EPC->callSPSWrapperAsync( + WrapperFnAddr, std::forward(SendResult), Args...); } /// Run a wrapper function using SPS to serialize the arguments and @@ -1506,13 +1501,10 @@ public: /// If SPSSignature is a non-void function signature then the second argument /// (the first in the Args list) should be a reference to a return value. template - Error callSPSWrapper(JITTargetAddress WrapperFnAddr, + Error callSPSWrapper(ExecutorAddr WrapperFnAddr, WrapperCallArgTs &&...WrapperCallArgs) { - return shared::WrapperFunction::call( - [this, WrapperFnAddr](const char *ArgData, size_t ArgSize) { - return callWrapper(WrapperFnAddr, ArrayRef(ArgData, ArgSize)); - }, - std::forward(WrapperCallArgs)...); + return EPC->callSPSWrapper( + WrapperFnAddr, std::forward(WrapperCallArgs)...); } /// Wrap a handler that takes concrete argument types (and a sender for a @@ -1525,7 +1517,7 @@ public: template static JITDispatchHandlerFunction wrapAsyncWithSPS(HandlerT &&H) { return [H = std::forward(H)]( - ExecutorProcessControl::SendResultFunction SendResult, + SendResultFunction SendResult, const char *ArgData, size_t ArgSize) mutable { shared::WrapperFunction::handleAsync(ArgData, ArgSize, H, std::move(SendResult)); @@ -1564,7 +1556,7 @@ public: /// This should be called by the ExecutorProcessControl instance in response /// to incoming jit-dispatch requests from the executor. void - runJITDispatchHandler(ExecutorProcessControl::SendResultFunction SendResult, + runJITDispatchHandler(SendResultFunction SendResult, JITTargetAddress HandlerFnTagAddr, ArrayRef ArgBuffer); diff --git a/llvm/include/llvm/ExecutionEngine/Orc/DebuggerSupportPlugin.h b/llvm/include/llvm/ExecutionEngine/Orc/DebuggerSupportPlugin.h new file mode 100644 index 000000000000..af092b3287d3 --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/Orc/DebuggerSupportPlugin.h @@ -0,0 +1,64 @@ +//===--- DebugerSupportPlugin.h -- Utils for debugger support ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Generates debug objects and registers them using the jit-loader-gdb protocol. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_ORC_DEBUGGERSUPPORT_H +#define LLVM_EXECUTIONENGINE_ORC_DEBUGGERSUPPORT_H + +#include "llvm/ExecutionEngine/Orc/Core.h" +#include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h" +#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h" + +namespace llvm { +namespace orc { + +/// For each object containing debug info, installs JITLink passes to synthesize +/// a debug object and then register it via the GDB JIT-registration interface. +/// +/// Currently MachO only. For ELF use DebugObjectManagerPlugin. These two +/// plugins will be merged in the near future. +class GDBJITDebugInfoRegistrationPlugin : public ObjectLinkingLayer::Plugin { +public: + class DebugSectionSynthesizer { + public: + virtual ~DebugSectionSynthesizer() {} + virtual Error startSynthesis() = 0; + virtual Error completeSynthesisAndRegister() = 0; + }; + + static Expected> + Create(ExecutionSession &ES, JITDylib &ProcessJD, const Triple &TT); + + GDBJITDebugInfoRegistrationPlugin(ExecutorAddr RegisterActionAddr) + : RegisterActionAddr(RegisterActionAddr) {} + + Error notifyFailed(MaterializationResponsibility &MR) override; + Error notifyRemovingResources(ResourceKey K) override; + + void notifyTransferringResources(ResourceKey DstKey, + ResourceKey SrcKey) override; + + void modifyPassConfig(MaterializationResponsibility &MR, + jitlink::LinkGraph &LG, + jitlink::PassConfiguration &PassConfig) override; + +private: + void modifyPassConfigForMachO(MaterializationResponsibility &MR, + jitlink::LinkGraph &LG, + jitlink::PassConfiguration &PassConfig); + + ExecutorAddr RegisterActionAddr; +}; + +} // namespace orc +} // namespace llvm + +#endif // LLVM_EXECUTIONENGINE_ORC_DEBUGGERSUPPORT_H diff --git a/llvm/include/llvm/ExecutionEngine/Orc/ELFNixPlatform.h b/llvm/include/llvm/ExecutionEngine/Orc/ELFNixPlatform.h new file mode 100644 index 000000000000..20da3e3b89eb --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/Orc/ELFNixPlatform.h @@ -0,0 +1,330 @@ +//===-- ELFNixPlatform.h -- Utilities for executing ELF in Orc --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Linux/BSD support for executing JIT'd ELF in Orc. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_ORC_ELFNIXPLATFORM_H +#define LLVM_EXECUTIONENGINE_ORC_ELFNIXPLATFORM_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/ExecutionEngine/Orc/Core.h" +#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h" +#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h" +#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h" + +#include +#include +#include + +namespace llvm { +namespace orc { + +struct ELFPerObjectSectionsToRegister { + ExecutorAddrRange EHFrameSection; + ExecutorAddrRange ThreadDataSection; +}; + +struct ELFNixJITDylibInitializers { + using SectionList = std::vector; + + ELFNixJITDylibInitializers(std::string Name, ExecutorAddr DSOHandleAddress) + : Name(std::move(Name)), DSOHandleAddress(std::move(DSOHandleAddress)) {} + + std::string Name; + ExecutorAddr DSOHandleAddress; + + StringMap InitSections; +}; + +class ELFNixJITDylibDeinitializers {}; + +using ELFNixJITDylibInitializerSequence = + std::vector; + +using ELFNixJITDylibDeinitializerSequence = + std::vector; + +/// Mediates between ELFNix initialization and ExecutionSession state. +class ELFNixPlatform : public Platform { +public: + /// Try to create a ELFNixPlatform instance, adding the ORC runtime to the + /// given JITDylib. + /// + /// The ORC runtime requires access to a number of symbols in + /// libc++. It is up to the caller to ensure that the requried + /// symbols can be referenced by code added to PlatformJD. The + /// standard way to achieve this is to first attach dynamic library + /// search generators for either the given process, or for the + /// specific required libraries, to PlatformJD, then to create the + /// platform instance: + /// + /// \code{.cpp} + /// auto &PlatformJD = ES.createBareJITDylib("stdlib"); + /// PlatformJD.addGenerator( + /// ExitOnErr(EPCDynamicLibrarySearchGenerator + /// ::GetForTargetProcess(EPC))); + /// ES.setPlatform( + /// ExitOnErr(ELFNixPlatform::Create(ES, ObjLayer, EPC, PlatformJD, + /// "/path/to/orc/runtime"))); + /// \endcode + /// + /// Alternatively, these symbols could be added to another JITDylib that + /// PlatformJD links against. + /// + /// Clients are also responsible for ensuring that any JIT'd code that + /// depends on runtime functions (including any code using TLV or static + /// destructors) can reference the runtime symbols. This is usually achieved + /// by linking any JITDylibs containing regular code against + /// PlatformJD. + /// + /// By default, ELFNixPlatform will add the set of aliases returned by the + /// standardPlatformAliases function. This includes both required aliases + /// (e.g. __cxa_atexit -> __orc_rt_elf_cxa_atexit for static destructor + /// support), and optional aliases that provide JIT versions of common + /// functions (e.g. dlopen -> __orc_rt_elf_jit_dlopen). Clients can + /// override these defaults by passing a non-None value for the + /// RuntimeAliases function, in which case the client is responsible for + /// setting up all aliases (including the required ones). + static Expected> + Create(ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer, + JITDylib &PlatformJD, const char *OrcRuntimePath, + Optional RuntimeAliases = None); + + ExecutionSession &getExecutionSession() const { return ES; } + ObjectLinkingLayer &getObjectLinkingLayer() const { return ObjLinkingLayer; } + + Error setupJITDylib(JITDylib &JD) override; + Error notifyAdding(ResourceTracker &RT, + const MaterializationUnit &MU) override; + Error notifyRemoving(ResourceTracker &RT) override; + + /// Returns an AliasMap containing the default aliases for the ELFNixPlatform. + /// This can be modified by clients when constructing the platform to add + /// or remove aliases. + static SymbolAliasMap standardPlatformAliases(ExecutionSession &ES); + + /// Returns the array of required CXX aliases. + static ArrayRef> requiredCXXAliases(); + + /// Returns the array of standard runtime utility aliases for ELF. + static ArrayRef> + standardRuntimeUtilityAliases(); + + /// Returns true if the given section name is an initializer section. + static bool isInitializerSection(StringRef SecName); + +private: + // The ELFNixPlatformPlugin scans/modifies LinkGraphs to support ELF + // platform features including initializers, exceptions, TLV, and language + // runtime registration. + class ELFNixPlatformPlugin : public ObjectLinkingLayer::Plugin { + public: + ELFNixPlatformPlugin(ELFNixPlatform &MP) : MP(MP) {} + + void modifyPassConfig(MaterializationResponsibility &MR, + jitlink::LinkGraph &G, + jitlink::PassConfiguration &Config) override; + + SyntheticSymbolDependenciesMap + getSyntheticSymbolDependencies(MaterializationResponsibility &MR) override; + + // FIXME: We should be tentatively tracking scraped sections and discarding + // if the MR fails. + Error notifyFailed(MaterializationResponsibility &MR) override { + return Error::success(); + } + + Error notifyRemovingResources(ResourceKey K) override { + return Error::success(); + } + + void notifyTransferringResources(ResourceKey DstKey, + ResourceKey SrcKey) override {} + + private: + using InitSymbolDepMap = + DenseMap; + + void addInitializerSupportPasses(MaterializationResponsibility &MR, + jitlink::PassConfiguration &Config); + + void addDSOHandleSupportPasses(MaterializationResponsibility &MR, + jitlink::PassConfiguration &Config); + + void addEHAndTLVSupportPasses(MaterializationResponsibility &MR, + jitlink::PassConfiguration &Config); + + Error preserveInitSections(jitlink::LinkGraph &G, + MaterializationResponsibility &MR); + + Error registerInitSections(jitlink::LinkGraph &G, JITDylib &JD); + + Error fixTLVSectionsAndEdges(jitlink::LinkGraph &G, JITDylib &JD); + + std::mutex PluginMutex; + ELFNixPlatform &MP; + InitSymbolDepMap InitSymbolDeps; + }; + + using SendInitializerSequenceFn = + unique_function)>; + + using SendDeinitializerSequenceFn = + unique_function)>; + + using SendSymbolAddressFn = unique_function)>; + + static bool supportedTarget(const Triple &TT); + + ELFNixPlatform(ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer, + JITDylib &PlatformJD, + std::unique_ptr OrcRuntimeGenerator, + Error &Err); + + // Associate ELFNixPlatform JIT-side runtime support functions with handlers. + Error associateRuntimeSupportFunctions(JITDylib &PlatformJD); + + void getInitializersBuildSequencePhase(SendInitializerSequenceFn SendResult, + JITDylib &JD, + std::vector DFSLinkOrder); + + void getInitializersLookupPhase(SendInitializerSequenceFn SendResult, + JITDylib &JD); + + void rt_getInitializers(SendInitializerSequenceFn SendResult, + StringRef JDName); + + void rt_getDeinitializers(SendDeinitializerSequenceFn SendResult, + ExecutorAddr Handle); + + void rt_lookupSymbol(SendSymbolAddressFn SendResult, ExecutorAddr Handle, + StringRef SymbolName); + + // Records the addresses of runtime symbols used by the platform. + Error bootstrapELFNixRuntime(JITDylib &PlatformJD); + + Error registerInitInfo(JITDylib &JD, + ArrayRef InitSections); + + Error registerPerObjectSections(const ELFPerObjectSectionsToRegister &POSR); + + Expected createPThreadKey(); + + ExecutionSession &ES; + ObjectLinkingLayer &ObjLinkingLayer; + + SymbolStringPtr DSOHandleSymbol; + std::atomic RuntimeBootstrapped{false}; + + ExecutorAddr orc_rt_elfnix_platform_bootstrap; + ExecutorAddr orc_rt_elfnix_platform_shutdown; + ExecutorAddr orc_rt_elfnix_register_object_sections; + ExecutorAddr orc_rt_elfnix_create_pthread_key; + + DenseMap RegisteredInitSymbols; + + // InitSeqs gets its own mutex to avoid locking the whole session when + // aggregating data from the jitlink. + std::mutex PlatformMutex; + DenseMap InitSeqs; + std::vector BootstrapPOSRs; + + DenseMap HandleAddrToJITDylib; + DenseMap JITDylibToPThreadKey; +}; + +namespace shared { + +using SPSELFPerObjectSectionsToRegister = + SPSTuple; + +template <> +class SPSSerializationTraits { + +public: + static size_t size(const ELFPerObjectSectionsToRegister &MOPOSR) { + return SPSELFPerObjectSectionsToRegister::AsArgList::size( + MOPOSR.EHFrameSection, MOPOSR.ThreadDataSection); + } + + static bool serialize(SPSOutputBuffer &OB, + const ELFPerObjectSectionsToRegister &MOPOSR) { + return SPSELFPerObjectSectionsToRegister::AsArgList::serialize( + OB, MOPOSR.EHFrameSection, MOPOSR.ThreadDataSection); + } + + static bool deserialize(SPSInputBuffer &IB, + ELFPerObjectSectionsToRegister &MOPOSR) { + return SPSELFPerObjectSectionsToRegister::AsArgList::deserialize( + IB, MOPOSR.EHFrameSection, MOPOSR.ThreadDataSection); + } +}; + +using SPSNamedExecutorAddrRangeSequenceMap = + SPSSequence>; + +using SPSELFNixJITDylibInitializers = + SPSTuple; + +using SPSELFNixJITDylibInitializerSequence = + SPSSequence; + +/// Serialization traits for ELFNixJITDylibInitializers. +template <> +class SPSSerializationTraits { +public: + static size_t size(const ELFNixJITDylibInitializers &MOJDIs) { + return SPSELFNixJITDylibInitializers::AsArgList::size( + MOJDIs.Name, MOJDIs.DSOHandleAddress, MOJDIs.InitSections); + } + + static bool serialize(SPSOutputBuffer &OB, + const ELFNixJITDylibInitializers &MOJDIs) { + return SPSELFNixJITDylibInitializers::AsArgList::serialize( + OB, MOJDIs.Name, MOJDIs.DSOHandleAddress, MOJDIs.InitSections); + } + + static bool deserialize(SPSInputBuffer &IB, + ELFNixJITDylibInitializers &MOJDIs) { + return SPSELFNixJITDylibInitializers::AsArgList::deserialize( + IB, MOJDIs.Name, MOJDIs.DSOHandleAddress, MOJDIs.InitSections); + } +}; + +using SPSELFJITDylibDeinitializers = SPSEmpty; + +using SPSELFJITDylibDeinitializerSequence = + SPSSequence; + +template <> +class SPSSerializationTraits { +public: + static size_t size(const ELFNixJITDylibDeinitializers &MOJDDs) { return 0; } + + static bool serialize(SPSOutputBuffer &OB, + const ELFNixJITDylibDeinitializers &MOJDDs) { + return true; + } + + static bool deserialize(SPSInputBuffer &IB, + ELFNixJITDylibDeinitializers &MOJDDs) { + MOJDDs = ELFNixJITDylibDeinitializers(); + return true; + } +}; + +} // end namespace shared +} // end namespace orc +} // end namespace llvm + +#endif // LLVM_EXECUTIONENGINE_ORC_ELFNIXPLATFORM_H diff --git a/llvm/include/llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h b/llvm/include/llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h index 410a202b3296..940d0d28ae83 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h @@ -14,6 +14,7 @@ #define LLVM_EXECUTIONENGINE_ORC_EPCDEBUGOBJECTREGISTRAR_H #include "llvm/ExecutionEngine/JITSymbol.h" +#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h" #include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h" #include "llvm/Support/Error.h" #include "llvm/Support/Memory.h" @@ -32,7 +33,7 @@ class ExecutionSession; /// Abstract interface for registering debug objects in the executor process. class DebugObjectRegistrar { public: - virtual Error registerDebugObject(sys::MemoryBlock) = 0; + virtual Error registerDebugObject(ExecutorAddrRange TargetMem) = 0; virtual ~DebugObjectRegistrar() {} }; @@ -40,14 +41,14 @@ public: /// executor process. class EPCDebugObjectRegistrar : public DebugObjectRegistrar { public: - EPCDebugObjectRegistrar(ExecutionSession &ES, JITTargetAddress RegisterFn) + EPCDebugObjectRegistrar(ExecutionSession &ES, ExecutorAddr RegisterFn) : ES(ES), RegisterFn(RegisterFn) {} - Error registerDebugObject(sys::MemoryBlock TargetMem) override; + Error registerDebugObject(ExecutorAddrRange TargetMem) override; private: ExecutionSession &ES; - JITTargetAddress RegisterFn; + ExecutorAddr RegisterFn; }; /// Create a ExecutorProcessControl-based DebugObjectRegistrar that emits debug diff --git a/llvm/include/llvm/ExecutionEngine/Orc/EPCEHFrameRegistrar.h b/llvm/include/llvm/ExecutionEngine/Orc/EPCEHFrameRegistrar.h index 8cd6e9319a28..6d113a7bdf1a 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/EPCEHFrameRegistrar.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/EPCEHFrameRegistrar.h @@ -14,6 +14,7 @@ #define LLVM_EXECUTIONENGINE_ORC_EPCEHFRAMEREGISTRAR_H #include "llvm/ExecutionEngine/JITLink/EHFrameSupport.h" +#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h" namespace llvm { namespace orc { @@ -33,8 +34,8 @@ public: /// Create a EPCEHFrameRegistrar with the given ExecutorProcessControl /// object and registration/deregistration function addresses. EPCEHFrameRegistrar(ExecutionSession &ES, - JITTargetAddress RegisterEHFrameWrapperFnAddr, - JITTargetAddress DeregisterEHFRameWrapperFnAddr) + ExecutorAddr RegisterEHFrameWrapperFnAddr, + ExecutorAddr DeregisterEHFRameWrapperFnAddr) : ES(ES), RegisterEHFrameWrapperFnAddr(RegisterEHFrameWrapperFnAddr), DeregisterEHFrameWrapperFnAddr(DeregisterEHFRameWrapperFnAddr) {} @@ -45,8 +46,8 @@ public: private: ExecutionSession &ES; - JITTargetAddress RegisterEHFrameWrapperFnAddr; - JITTargetAddress DeregisterEHFrameWrapperFnAddr; + ExecutorAddr RegisterEHFrameWrapperFnAddr; + ExecutorAddr DeregisterEHFrameWrapperFnAddr; }; } // end namespace orc diff --git a/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericDylibManager.h b/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericDylibManager.h new file mode 100644 index 000000000000..02e580c86f54 --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericDylibManager.h @@ -0,0 +1,67 @@ +//===- EPCGenericDylibManager.h -- Generic EPC Dylib management -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implements dylib loading and searching by making calls to +// ExecutorProcessControl::callWrapper. +// +// This simplifies the implementaton of new ExecutorProcessControl instances, +// as this implementation will always work (at the cost of some performance +// overhead for the calls). +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_ORC_EPCGENERICDYLIBMANAGER_H +#define LLVM_EXECUTIONENGINE_ORC_EPCGENERICDYLIBMANAGER_H + +#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h" +#include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h" + +namespace llvm { +namespace orc { + +class SymbolLookupSet; + +class EPCGenericDylibManager { +public: + /// Function addresses for memory access. + struct SymbolAddrs { + ExecutorAddr Instance; + ExecutorAddr Open; + ExecutorAddr Lookup; + }; + + /// Create an EPCGenericMemoryAccess instance from a given set of + /// function addrs. + static Expected + CreateWithDefaultBootstrapSymbols(ExecutorProcessControl &EPC); + + /// Create an EPCGenericMemoryAccess instance from a given set of + /// function addrs. + EPCGenericDylibManager(ExecutorProcessControl &EPC, SymbolAddrs SAs) + : EPC(EPC), SAs(SAs) {} + + /// Loads the dylib with the given name. + Expected open(StringRef Path, uint64_t Mode); + + /// Looks up symbols within the given dylib. + Expected> lookup(tpctypes::DylibHandle H, + const SymbolLookupSet &Lookup); + + /// Looks up symbols within the given dylib. + Expected> + lookup(tpctypes::DylibHandle H, const RemoteSymbolLookupSet &Lookup); + +private: + ExecutorProcessControl &EPC; + SymbolAddrs SAs; +}; + +} // end namespace orc +} // end namespace llvm + +#endif // LLVM_EXECUTIONENGINE_ORC_EPCGENERICDYLIBMANAGER_H diff --git a/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.h b/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.h new file mode 100644 index 000000000000..b9825f17ec17 --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.h @@ -0,0 +1,97 @@ +//===- EPCGenericJITLinkMemoryManager.h - EPC-based mem manager -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implements JITLinkMemoryManager by making remove calls via +// ExecutorProcessControl::callWrapperAsync. +// +// This simplifies the implementaton of new ExecutorProcessControl instances, +// as this implementation will always work (at the cost of some performance +// overhead for the calls). +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_ORC_EPCGENERICJITLINKMEMORYMANAGER_H +#define LLVM_EXECUTIONENGINE_ORC_EPCGENERICJITLINKMEMORYMANAGER_H + +#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h" +#include "llvm/ExecutionEngine/Orc/Core.h" + +namespace llvm { +namespace orc { + +class EPCGenericJITLinkMemoryManager : public jitlink::JITLinkMemoryManager { +public: + /// Function addresses for memory access. + struct SymbolAddrs { + ExecutorAddr Allocator; + ExecutorAddr Reserve; + ExecutorAddr Finalize; + ExecutorAddr Deallocate; + }; + + /// Create an EPCGenericJITLinkMemoryManager instance from a given set of + /// function addrs. + EPCGenericJITLinkMemoryManager(ExecutorProcessControl &EPC, SymbolAddrs SAs) + : EPC(EPC), SAs(SAs) {} + + void allocate(const jitlink::JITLinkDylib *JD, jitlink::LinkGraph &G, + OnAllocatedFunction OnAllocated) override; + + // Use overloads from base class. + using JITLinkMemoryManager::allocate; + + void deallocate(std::vector Allocs, + OnDeallocatedFunction OnDeallocated) override; + + // Use overloads from base class. + using JITLinkMemoryManager::deallocate; + +private: + class InFlightAlloc; + + void completeAllocation(ExecutorAddr AllocAddr, jitlink::BasicLayout BL, + OnAllocatedFunction OnAllocated); + + ExecutorProcessControl &EPC; + SymbolAddrs SAs; +}; + +namespace shared { + +/// FIXME: This specialization should be moved into TargetProcessControlTypes.h +/// (or whereever those types get merged to) once ORC depends on JITLink. +template <> +class SPSSerializationTraits { +public: + static size_t size(const jitlink::JITLinkMemoryManager::FinalizedAlloc &FA) { + return SPSArgList::size(ExecutorAddr(FA.getAddress())); + } + + static bool + serialize(SPSOutputBuffer &OB, + const jitlink::JITLinkMemoryManager::FinalizedAlloc &FA) { + return SPSArgList::serialize( + OB, ExecutorAddr(FA.getAddress())); + } + + static bool deserialize(SPSInputBuffer &IB, + jitlink::JITLinkMemoryManager::FinalizedAlloc &FA) { + ExecutorAddr A; + if (!SPSArgList::deserialize(IB, A)) + return false; + FA = jitlink::JITLinkMemoryManager::FinalizedAlloc(A.getValue()); + return true; + } +}; + +} // end namespace shared +} // end namespace orc +} // end namespace llvm + +#endif // LLVM_EXECUTIONENGINE_ORC_EPCGENERICJITLINKMEMORYMANAGER_H diff --git a/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericMemoryAccess.h b/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericMemoryAccess.h new file mode 100644 index 000000000000..8c1d457d06ab --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericMemoryAccess.h @@ -0,0 +1,85 @@ +//===- EPCGenericMemoryAccess.h - Generic EPC MemoryAccess impl -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implements ExecutorProcessControl::MemoryAccess by making calls to +// ExecutorProcessControl::callWrapperAsync. +// +// This simplifies the implementaton of new ExecutorProcessControl instances, +// as this implementation will always work (at the cost of some performance +// overhead for the calls). +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_ORC_EPCGENERICMEMORYACCESS_H +#define LLVM_EXECUTIONENGINE_ORC_EPCGENERICMEMORYACCESS_H + +#include "llvm/ExecutionEngine/Orc/Core.h" + +namespace llvm { +namespace orc { + +class EPCGenericMemoryAccess : public ExecutorProcessControl::MemoryAccess { +public: + /// Function addresses for memory access. + struct FuncAddrs { + ExecutorAddr WriteUInt8s; + ExecutorAddr WriteUInt16s; + ExecutorAddr WriteUInt32s; + ExecutorAddr WriteUInt64s; + ExecutorAddr WriteBuffers; + }; + + /// Create an EPCGenericMemoryAccess instance from a given set of + /// function addrs. + EPCGenericMemoryAccess(ExecutorProcessControl &EPC, FuncAddrs FAs) + : EPC(EPC), FAs(FAs) {} + + void writeUInt8sAsync(ArrayRef Ws, + WriteResultFn OnWriteComplete) override { + using namespace shared; + EPC.callSPSWrapperAsync)>( + FAs.WriteUInt8s, std::move(OnWriteComplete), Ws); + } + + void writeUInt16sAsync(ArrayRef Ws, + WriteResultFn OnWriteComplete) override { + using namespace shared; + EPC.callSPSWrapperAsync)>( + FAs.WriteUInt16s, std::move(OnWriteComplete), Ws); + } + + void writeUInt32sAsync(ArrayRef Ws, + WriteResultFn OnWriteComplete) override { + using namespace shared; + EPC.callSPSWrapperAsync)>( + FAs.WriteUInt32s, std::move(OnWriteComplete), Ws); + } + + void writeUInt64sAsync(ArrayRef Ws, + WriteResultFn OnWriteComplete) override { + using namespace shared; + EPC.callSPSWrapperAsync)>( + FAs.WriteUInt64s, std::move(OnWriteComplete), Ws); + } + + void writeBuffersAsync(ArrayRef Ws, + WriteResultFn OnWriteComplete) override { + using namespace shared; + EPC.callSPSWrapperAsync)>( + FAs.WriteBuffers, std::move(OnWriteComplete), Ws); + } + +private: + ExecutorProcessControl &EPC; + FuncAddrs FAs; +}; + +} // end namespace orc +} // end namespace llvm + +#endif // LLVM_EXECUTIONENGINE_ORC_EPCGENERICMEMORYACCESS_H diff --git a/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericRTDyldMemoryManager.h b/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericRTDyldMemoryManager.h new file mode 100644 index 000000000000..b6fdfb92ced3 --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericRTDyldMemoryManager.h @@ -0,0 +1,133 @@ +//===---- EPCGenericRTDyldMemoryManager.h - EPC-based MemMgr ----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines a RuntimeDyld::MemoryManager that uses EPC and the ORC runtime +// bootstrap functions. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_ORC_EPCGENERICRTDYLDMEMORYMANAGER_H +#define LLVM_EXECUTIONENGINE_ORC_EPCGENERICRTDYLDMEMORYMANAGER_H + +#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h" +#include "llvm/ExecutionEngine/RuntimeDyld.h" + +#define DEBUG_TYPE "orc" + +namespace llvm { +namespace orc { + +/// Remote-mapped RuntimeDyld-compatible memory manager. +class EPCGenericRTDyldMemoryManager : public RuntimeDyld::MemoryManager { +public: + /// Symbol addresses for memory access. + struct SymbolAddrs { + ExecutorAddr Instance; + ExecutorAddr Reserve; + ExecutorAddr Finalize; + ExecutorAddr Deallocate; + ExecutorAddr RegisterEHFrame; + ExecutorAddr DeregisterEHFrame; + }; + + /// Create an EPCGenericRTDyldMemoryManager using the given EPC, looking up + /// the default symbol names in the bootstrap symbol set. + static Expected> + CreateWithDefaultBootstrapSymbols(ExecutorProcessControl &EPC); + + /// Create an EPCGenericRTDyldMemoryManager using the given EPC and symbol + /// addrs. + EPCGenericRTDyldMemoryManager(ExecutorProcessControl &EPC, SymbolAddrs SAs); + + EPCGenericRTDyldMemoryManager(const EPCGenericRTDyldMemoryManager &) = delete; + EPCGenericRTDyldMemoryManager & + operator=(const EPCGenericRTDyldMemoryManager &) = delete; + EPCGenericRTDyldMemoryManager(EPCGenericRTDyldMemoryManager &&) = delete; + EPCGenericRTDyldMemoryManager & + operator=(EPCGenericRTDyldMemoryManager &&) = delete; + ~EPCGenericRTDyldMemoryManager(); + + uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, + unsigned SectionID, + StringRef SectionName) override; + + uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, + unsigned SectionID, StringRef SectionName, + bool IsReadOnly) override; + + void reserveAllocationSpace(uintptr_t CodeSize, uint32_t CodeAlign, + uintptr_t RODataSize, uint32_t RODataAlign, + uintptr_t RWDataSize, + uint32_t RWDataAlign) override; + + bool needsToReserveAllocationSpace() override; + + void registerEHFrames(uint8_t *Addr, uint64_t LoadAddr, size_t Size) override; + + void deregisterEHFrames() override; + + void notifyObjectLoaded(RuntimeDyld &Dyld, + const object::ObjectFile &Obj) override; + + bool finalizeMemory(std::string *ErrMsg = nullptr) override; + +private: + struct Alloc { + public: + Alloc(uint64_t Size, unsigned Align) + : Size(Size), Align(Align), + Contents(std::make_unique(Size + Align - 1)) {} + + uint64_t Size; + unsigned Align; + std::unique_ptr Contents; + ExecutorAddr RemoteAddr; + }; + + struct EHFrame { + ExecutorAddr Addr; + uint64_t Size; + }; + + // Group of section allocations to be allocated together in the executor. The + // RemoteCodeAddr will stand in as the id of the group for deallocation + // purposes. + struct AllocGroup { + AllocGroup() = default; + AllocGroup(const AllocGroup &) = delete; + AllocGroup &operator=(const AllocGroup &) = delete; + AllocGroup(AllocGroup &&) = default; + AllocGroup &operator=(AllocGroup &&) = default; + + ExecutorAddrRange RemoteCode; + ExecutorAddrRange RemoteROData; + ExecutorAddrRange RemoteRWData; + std::vector UnfinalizedEHFrames; + std::vector CodeAllocs, RODataAllocs, RWDataAllocs; + }; + + // Maps all allocations in Allocs to aligned blocks + void mapAllocsToRemoteAddrs(RuntimeDyld &Dyld, std::vector &Allocs, + ExecutorAddr NextAddr); + + ExecutorProcessControl &EPC; + SymbolAddrs SAs; + + std::mutex M; + std::vector Unmapped; + std::vector Unfinalized; + std::vector FinalizedAllocs; + std::string ErrMsg; +}; + +} // end namespace orc +} // end namespace llvm + +#undef DEBUG_TYPE + +#endif // LLVM_EXECUTIONENGINE_ORC_EPCGENERICRTDYLDMEMORYMANAGER_H diff --git a/llvm/include/llvm/ExecutionEngine/Orc/EPCIndirectionUtils.h b/llvm/include/llvm/ExecutionEngine/Orc/EPCIndirectionUtils.h index 64f16d507c97..92de5882bafe 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/EPCIndirectionUtils.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/EPCIndirectionUtils.h @@ -126,7 +126,7 @@ public: } private: - using Allocation = jitlink::JITLinkMemoryManager::Allocation; + using FinalizedAlloc = jitlink::JITLinkMemoryManager::FinalizedAlloc; struct IndirectStubInfo { IndirectStubInfo() = default; @@ -149,12 +149,12 @@ private: ExecutorProcessControl &EPC; std::unique_ptr ABI; JITTargetAddress ResolverBlockAddr; - std::unique_ptr ResolverBlock; + FinalizedAlloc ResolverBlock; std::unique_ptr TP; std::unique_ptr LCTM; std::vector AvailableIndirectStubs; - std::vector> IndirectStubAllocs; + std::vector IndirectStubAllocs; }; /// This will call writeResolver on the given EPCIndirectionUtils instance diff --git a/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h b/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h index d540d0cd0608..105dac8e8d04 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h @@ -13,7 +13,6 @@ #ifndef LLVM_EXECUTIONENGINE_ORC_EXECUTORPROCESSCONTROL_H #define LLVM_EXECUTIONENGINE_ORC_EXECUTORPROCESSCONTROL_H -#include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" #include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h" @@ -21,6 +20,7 @@ #include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h" #include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h" #include "llvm/ExecutionEngine/Orc/SymbolStringPool.h" +#include "llvm/ExecutionEngine/Orc/TaskDispatch.h" #include "llvm/Support/DynamicLibrary.h" #include "llvm/Support/MSVCErrorWorkarounds.h" @@ -37,11 +37,65 @@ class SymbolLookupSet; /// ExecutorProcessControl supports interaction with a JIT target process. class ExecutorProcessControl { friend class ExecutionSession; - public: - /// Sender to return the result of a WrapperFunction executed in the JIT. - using SendResultFunction = - unique_function; + + /// A handler or incoming WrapperFunctionResults -- either return values from + /// callWrapper* calls, or incoming JIT-dispatch requests. + /// + /// IncomingWFRHandlers are constructible from + /// unique_functions using the + /// runInPlace function or a RunWithDispatch object. + class IncomingWFRHandler { + friend class ExecutorProcessControl; + public: + IncomingWFRHandler() = default; + explicit operator bool() const { return !!H; } + void operator()(shared::WrapperFunctionResult WFR) { H(std::move(WFR)); } + private: + template IncomingWFRHandler(FnT &&Fn) + : H(std::forward(Fn)) {} + + unique_function H; + }; + + /// Constructs an IncomingWFRHandler from a function object that is callable + /// as void(shared::WrapperFunctionResult). The function object will be called + /// directly. This should be used with care as it may block listener threads + /// in remote EPCs. It is only suitable for simple tasks (e.g. setting a + /// future), or for performing some quick analysis before dispatching "real" + /// work as a Task. + class RunInPlace { + public: + template + IncomingWFRHandler operator()(FnT &&Fn) { + return IncomingWFRHandler(std::forward(Fn)); + } + }; + + /// Constructs an IncomingWFRHandler from a function object by creating a new + /// function object that dispatches the original using a TaskDispatcher, + /// wrapping the original as a GenericNamedTask. + /// + /// This is the default approach for running WFR handlers. + class RunAsTask { + public: + RunAsTask(TaskDispatcher &D) : D(D) {} + + template + IncomingWFRHandler operator()(FnT &&Fn) { + return IncomingWFRHandler( + [&D = this->D, Fn = std::move(Fn)] + (shared::WrapperFunctionResult WFR) mutable { + D.dispatch( + makeGenericNamedTask( + [Fn = std::move(Fn), WFR = std::move(WFR)]() mutable { + Fn(std::move(WFR)); + }, "WFR handler task")); + }); + } + private: + TaskDispatcher &D; + }; /// APIs for manipulating memory in the target process. class MemoryAccess { @@ -51,53 +105,58 @@ public: virtual ~MemoryAccess(); - virtual void writeUInt8s(ArrayRef Ws, - WriteResultFn OnWriteComplete) = 0; + virtual void writeUInt8sAsync(ArrayRef Ws, + WriteResultFn OnWriteComplete) = 0; - virtual void writeUInt16s(ArrayRef Ws, - WriteResultFn OnWriteComplete) = 0; + virtual void writeUInt16sAsync(ArrayRef Ws, + WriteResultFn OnWriteComplete) = 0; - virtual void writeUInt32s(ArrayRef Ws, - WriteResultFn OnWriteComplete) = 0; + virtual void writeUInt32sAsync(ArrayRef Ws, + WriteResultFn OnWriteComplete) = 0; - virtual void writeUInt64s(ArrayRef Ws, - WriteResultFn OnWriteComplete) = 0; + virtual void writeUInt64sAsync(ArrayRef Ws, + WriteResultFn OnWriteComplete) = 0; - virtual void writeBuffers(ArrayRef Ws, - WriteResultFn OnWriteComplete) = 0; + virtual void writeBuffersAsync(ArrayRef Ws, + WriteResultFn OnWriteComplete) = 0; Error writeUInt8s(ArrayRef Ws) { std::promise ResultP; auto ResultF = ResultP.get_future(); - writeUInt8s(Ws, [&](Error Err) { ResultP.set_value(std::move(Err)); }); + writeUInt8sAsync(Ws, + [&](Error Err) { ResultP.set_value(std::move(Err)); }); return ResultF.get(); } Error writeUInt16s(ArrayRef Ws) { std::promise ResultP; auto ResultF = ResultP.get_future(); - writeUInt16s(Ws, [&](Error Err) { ResultP.set_value(std::move(Err)); }); + writeUInt16sAsync(Ws, + [&](Error Err) { ResultP.set_value(std::move(Err)); }); return ResultF.get(); } Error writeUInt32s(ArrayRef Ws) { std::promise ResultP; auto ResultF = ResultP.get_future(); - writeUInt32s(Ws, [&](Error Err) { ResultP.set_value(std::move(Err)); }); + writeUInt32sAsync(Ws, + [&](Error Err) { ResultP.set_value(std::move(Err)); }); return ResultF.get(); } Error writeUInt64s(ArrayRef Ws) { std::promise ResultP; auto ResultF = ResultP.get_future(); - writeUInt64s(Ws, [&](Error Err) { ResultP.set_value(std::move(Err)); }); + writeUInt64sAsync(Ws, + [&](Error Err) { ResultP.set_value(std::move(Err)); }); return ResultF.get(); } Error writeBuffers(ArrayRef Ws) { std::promise ResultP; auto ResultF = ResultP.get_future(); - writeBuffers(Ws, [&](Error Err) { ResultP.set_value(std::move(Err)); }); + writeBuffersAsync(Ws, + [&](Error Err) { ResultP.set_value(std::move(Err)); }); return ResultF.get(); } }; @@ -113,10 +172,14 @@ public: /// Contains the address of the dispatch function and context that the ORC /// runtime can use to call functions in the JIT. struct JITDispatchInfo { - ExecutorAddress JITDispatchFunctionAddress; - ExecutorAddress JITDispatchContextAddress; + ExecutorAddr JITDispatchFunction; + ExecutorAddr JITDispatchContext; }; + ExecutorProcessControl(std::shared_ptr SSP, + std::unique_ptr D) + : SSP(std::move(SSP)), D(std::move(D)) {} + virtual ~ExecutorProcessControl(); /// Return the ExecutionSession associated with this instance. @@ -132,6 +195,8 @@ public: /// Return a shared pointer to the SymbolStringPool for this instance. std::shared_ptr getSymbolStringPool() const { return SSP; } + TaskDispatcher &getDispatcher() { return *D; } + /// Return the Triple for the target process. const Triple &getTargetTriple() const { return TargetTriple; } @@ -153,6 +218,29 @@ public: return *MemMgr; } + /// Returns the bootstrap symbol map. + const StringMap &getBootstrapSymbolsMap() const { + return BootstrapSymbols; + } + + /// For each (ExecutorAddr&, StringRef) pair, looks up the string in the + /// bootstrap symbols map and writes its address to the ExecutorAddr if + /// found. If any symbol is not found then the function returns an error. + Error getBootstrapSymbols( + ArrayRef> Pairs) const { + for (auto &KV : Pairs) { + auto I = BootstrapSymbols.find(KV.second); + if (I == BootstrapSymbols.end()) + return make_error("Symbol \"" + KV.second + + "\" not found " + "in bootstrap symbols map", + inconvertibleErrorCode()); + + KV.first = I->second; + } + return Error::success(); + } + /// Load the dynamic library at the given path and return a handle to it. /// If LibraryPath is null this function will return the global handle for /// the target process. @@ -163,44 +251,119 @@ public: /// The result of the lookup is a 2-dimentional array of target addresses /// that correspond to the lookup order. If a required symbol is not /// found then this method will return an error. If a weakly referenced - /// symbol is not found then it be assigned a '0' value in the result. - /// that correspond to the lookup order. + /// symbol is not found then it be assigned a '0' value. virtual Expected> lookupSymbols(ArrayRef Request) = 0; /// Run function with a main-like signature. - virtual Expected runAsMain(JITTargetAddress MainFnAddr, + virtual Expected runAsMain(ExecutorAddr MainFnAddr, ArrayRef Args) = 0; - /// Run a wrapper function in the executor. + /// Run a wrapper function in the executor. The given WFRHandler will be + /// called on the result when it is returned. /// /// The wrapper function should be callable as: /// /// \code{.cpp} /// CWrapperFunctionResult fn(uint8_t *Data, uint64_t Size); /// \endcode{.cpp} - /// - /// The given OnComplete function will be called to return the result. - virtual void callWrapperAsync(SendResultFunction OnComplete, - JITTargetAddress WrapperFnAddr, + virtual void callWrapperAsync(ExecutorAddr WrapperFnAddr, + IncomingWFRHandler OnComplete, ArrayRef ArgBuffer) = 0; + /// Run a wrapper function in the executor using the given Runner to dispatch + /// OnComplete when the result is ready. + template + void callWrapperAsync(RunPolicyT &&Runner, ExecutorAddr WrapperFnAddr, + FnT &&OnComplete, ArrayRef ArgBuffer) { + callWrapperAsync( + WrapperFnAddr, Runner(std::forward(OnComplete)), ArgBuffer); + } + + /// Run a wrapper function in the executor. OnComplete will be dispatched + /// as a GenericNamedTask using this instance's TaskDispatch object. + template + void callWrapperAsync(ExecutorAddr WrapperFnAddr, FnT &&OnComplete, + ArrayRef ArgBuffer) { + callWrapperAsync(RunAsTask(*D), WrapperFnAddr, + std::forward(OnComplete), ArgBuffer); + } + + /// Run a wrapper function in the executor. The wrapper function should be + /// callable as: + /// + /// \code{.cpp} + /// CWrapperFunctionResult fn(uint8_t *Data, uint64_t Size); + /// \endcode{.cpp} + shared::WrapperFunctionResult callWrapper(ExecutorAddr WrapperFnAddr, + ArrayRef ArgBuffer) { + std::promise RP; + auto RF = RP.get_future(); + callWrapperAsync( + RunInPlace(), WrapperFnAddr, + [&](shared::WrapperFunctionResult R) { + RP.set_value(std::move(R)); + }, ArgBuffer); + return RF.get(); + } + + /// Run a wrapper function using SPS to serialize the arguments and + /// deserialize the results. + template + void callSPSWrapperAsync(RunPolicyT &&Runner, ExecutorAddr WrapperFnAddr, + SendResultT &&SendResult, const ArgTs &...Args) { + shared::WrapperFunction::callAsync( + [this, WrapperFnAddr, Runner = std::move(Runner)] + (auto &&SendResult, const char *ArgData, size_t ArgSize) mutable { + this->callWrapperAsync(std::move(Runner), WrapperFnAddr, + std::move(SendResult), + ArrayRef(ArgData, ArgSize)); + }, + std::forward(SendResult), Args...); + } + + /// Run a wrapper function using SPS to serialize the arguments and + /// deserialize the results. + template + void callSPSWrapperAsync(ExecutorAddr WrapperFnAddr, SendResultT &&SendResult, + const ArgTs &...Args) { + callSPSWrapperAsync(RunAsTask(*D), WrapperFnAddr, + std::forward(SendResult), + Args...); + } + + /// Run a wrapper function using SPS to serialize the arguments and + /// deserialize the results. + /// + /// If SPSSignature is a non-void function signature then the second argument + /// (the first in the Args list) should be a reference to a return value. + template + Error callSPSWrapper(ExecutorAddr WrapperFnAddr, + WrapperCallArgTs &&...WrapperCallArgs) { + return shared::WrapperFunction::call( + [this, WrapperFnAddr](const char *ArgData, size_t ArgSize) { + return callWrapper(WrapperFnAddr, ArrayRef(ArgData, ArgSize)); + }, + std::forward(WrapperCallArgs)...); + } + /// Disconnect from the target process. /// /// This should be called after the JIT session is shut down. virtual Error disconnect() = 0; protected: - ExecutorProcessControl(std::shared_ptr SSP) - : SSP(std::move(SSP)) {} std::shared_ptr SSP; + std::unique_ptr D; ExecutionSession *ES = nullptr; Triple TargetTriple; unsigned PageSize = 0; JITDispatchInfo JDI; MemoryAccess *MemAccess = nullptr; jitlink::JITLinkMemoryManager *MemMgr = nullptr; + StringMap BootstrapSymbols; }; /// A ExecutorProcessControl instance that asserts if any of its methods are @@ -210,9 +373,12 @@ class UnsupportedExecutorProcessControl : public ExecutorProcessControl { public: UnsupportedExecutorProcessControl( std::shared_ptr SSP = nullptr, + std::unique_ptr D = nullptr, const std::string &TT = "", unsigned PageSize = 0) : ExecutorProcessControl(SSP ? std::move(SSP) - : std::make_shared()) { + : std::make_shared(), + D ? std::move(D) + : std::make_unique()) { this->TargetTriple = Triple(TT); this->PageSize = PageSize; } @@ -226,13 +392,13 @@ public: llvm_unreachable("Unsupported"); } - Expected runAsMain(JITTargetAddress MainFnAddr, + Expected runAsMain(ExecutorAddr MainFnAddr, ArrayRef Args) override { llvm_unreachable("Unsupported"); } - void callWrapperAsync(SendResultFunction OnComplete, - JITTargetAddress WrapperFnAddr, + void callWrapperAsync(ExecutorAddr WrapperFnAddr, + IncomingWFRHandler OnComplete, ArrayRef ArgBuffer) override { llvm_unreachable("Unsupported"); } @@ -246,8 +412,9 @@ class SelfExecutorProcessControl private ExecutorProcessControl::MemoryAccess { public: SelfExecutorProcessControl( - std::shared_ptr SSP, Triple TargetTriple, - unsigned PageSize, std::unique_ptr MemMgr); + std::shared_ptr SSP, std::unique_ptr D, + Triple TargetTriple, unsigned PageSize, + std::unique_ptr MemMgr); /// Create a SelfExecutorProcessControl with the given symbol string pool and /// memory manager. @@ -256,6 +423,7 @@ public: /// be created and used by default. static Expected> Create(std::shared_ptr SSP = nullptr, + std::unique_ptr D = nullptr, std::unique_ptr MemMgr = nullptr); Expected loadDylib(const char *DylibPath) override; @@ -263,32 +431,32 @@ public: Expected> lookupSymbols(ArrayRef Request) override; - Expected runAsMain(JITTargetAddress MainFnAddr, + Expected runAsMain(ExecutorAddr MainFnAddr, ArrayRef Args) override; - void callWrapperAsync(SendResultFunction OnComplete, - JITTargetAddress WrapperFnAddr, + void callWrapperAsync(ExecutorAddr WrapperFnAddr, + IncomingWFRHandler OnComplete, ArrayRef ArgBuffer) override; Error disconnect() override; private: - void writeUInt8s(ArrayRef Ws, - WriteResultFn OnWriteComplete) override; + void writeUInt8sAsync(ArrayRef Ws, + WriteResultFn OnWriteComplete) override; - void writeUInt16s(ArrayRef Ws, - WriteResultFn OnWriteComplete) override; + void writeUInt16sAsync(ArrayRef Ws, + WriteResultFn OnWriteComplete) override; - void writeUInt32s(ArrayRef Ws, - WriteResultFn OnWriteComplete) override; + void writeUInt32sAsync(ArrayRef Ws, + WriteResultFn OnWriteComplete) override; - void writeUInt64s(ArrayRef Ws, - WriteResultFn OnWriteComplete) override; + void writeUInt64sAsync(ArrayRef Ws, + WriteResultFn OnWriteComplete) override; - void writeBuffers(ArrayRef Ws, - WriteResultFn OnWriteComplete) override; + void writeBuffersAsync(ArrayRef Ws, + WriteResultFn OnWriteComplete) override; - static shared::detail::CWrapperFunctionResult + static shared::CWrapperFunctionResult jitDispatchViaWrapperFunctionManager(void *Ctx, const void *FnTag, const char *Data, size_t Size); diff --git a/llvm/include/llvm/ExecutionEngine/Orc/IndirectionUtils.h b/llvm/include/llvm/ExecutionEngine/Orc/IndirectionUtils.h index 78e3ceef50e2..4d6d46595fc3 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/IndirectionUtils.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/IndirectionUtils.h @@ -45,6 +45,13 @@ class PointerType; class Triple; class Twine; class Value; +class MCDisassembler; +class MCInstrAnalysis; + +namespace jitlink { +class LinkGraph; +class Symbol; +} // namespace jitlink namespace orc { @@ -557,6 +564,33 @@ GlobalAlias *cloneGlobalAliasDecl(Module &Dst, const GlobalAlias &OrigA, void cloneModuleFlagsMetadata(Module &Dst, const Module &Src, ValueToValueMapTy &VMap); +/// Introduce relocations to \p Sym in its own definition if there are any +/// pointers formed via PC-relative address that do not already have a +/// relocation. +/// +/// This is useful when introducing indirection via a stub function at link time +/// without compiler support. If a function pointer is formed without a +/// relocation, e.g. in the definition of \c foo +/// +/// \code +/// _foo: +/// leaq -7(%rip), rax # form pointer to _foo without relocation +/// _bar: +/// leaq (%rip), %rax # uses X86_64_RELOC_SIGNED to '_foo' +/// \endcode +/// +/// the pointer to \c _foo computed by \c _foo and \c _bar may differ if we +/// introduce a stub for _foo. If the pointer is used as a key, this may be +/// observable to the program. This pass will attempt to introduce the missing +/// "self-relocation" on the leaq instruction. +/// +/// This is based on disassembly and should be considered "best effort". It may +/// silently fail to add relocations. +Error addFunctionPointerRelocationsToCurrentSymbol(jitlink::Symbol &Sym, + jitlink::LinkGraph &G, + MCDisassembler &Disassembler, + MCInstrAnalysis &MIA); + } // end namespace orc } // end namespace llvm diff --git a/llvm/include/llvm/ExecutionEngine/Orc/LLVMSPSSerializers.h b/llvm/include/llvm/ExecutionEngine/Orc/LLVMSPSSerializers.h deleted file mode 100644 index f3d616deae8f..000000000000 --- a/llvm/include/llvm/ExecutionEngine/Orc/LLVMSPSSerializers.h +++ /dev/null @@ -1,69 +0,0 @@ -//===-- LLVMSPSSerializers.h - SPS serialization for LLVM types -*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// SPS Serialization for common LLVM types. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_EXECUTIONENGINE_ORC_LLVMSPSSERIALIZERS_H -#define LLVM_EXECUTIONENGINE_ORC_LLVMSPSSERIALIZERS_H - -#include "llvm/ADT/StringMap.h" -#include "llvm/ExecutionEngine/Orc/Shared/SimplePackedSerialization.h" - -namespace llvm { -namespace orc { -namespace shared { - -template -class SPSSerializationTraits>, - StringMap> { -public: - static size_t size(const StringMap &M) { - size_t Sz = SPSArgList::size(static_cast(M.size())); - for (auto &E : M) - Sz += SPSArgList::size(E.first(), E.second); - return Sz; - } - - static bool serialize(SPSOutputBuffer &OB, const StringMap &M) { - if (!SPSArgList::serialize(OB, static_cast(M.size()))) - return false; - - for (auto &E : M) - if (!SPSArgList::serialize(OB, E.first(), E.second)) - return false; - - return true; - } - - static bool deserialize(SPSInputBuffer &IB, StringMap &M) { - uint64_t Size; - assert(M.empty() && "M already contains elements"); - - if (!SPSArgList::deserialize(IB, Size)) - return false; - - while (Size--) { - StringRef S; - ValueT V; - if (!SPSArgList::deserialize(IB, S, V)) - return false; - if (!M.insert(std::make_pair(S, V)).second) - return false; - } - - return true; - } -}; - -} // end namespace shared -} // end namespace orc -} // end namespace llvm - -#endif // LLVM_EXECUTIONENGINE_ORC_LLVMSPSSERIALIZERS_H diff --git a/llvm/include/llvm/ExecutionEngine/Orc/LookupAndRecordAddrs.h b/llvm/include/llvm/ExecutionEngine/Orc/LookupAndRecordAddrs.h new file mode 100644 index 000000000000..a598405ee4f6 --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/Orc/LookupAndRecordAddrs.h @@ -0,0 +1,70 @@ +//===-- LookupAndRecordAddrs.h - Symbol lookup support utility --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Record the addresses of a set of symbols into ExecutorAddr objects. +// +// This can be used to avoid repeated lookup (via ExecutionSession::lookup) of +// the given symbols. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_ORC_LOOKUPANDRECORDADDRS_H +#define LLVM_EXECUTIONENGINE_ORC_LOOKUPANDRECORDADDRS_H + +#include "llvm/ADT/FunctionExtras.h" +#include "llvm/ExecutionEngine/Orc/Core.h" +#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h" + +#include + +namespace llvm { +namespace orc { + +/// Record addresses of the given symbols in the given ExecutorAddrs. +/// +/// Useful for making permanent records of symbol addreses to call or +/// access in the executor (e.g. runtime support functions in Platform +/// subclasses). +/// +/// By default the symbols are looked up using +/// SymbolLookupFlags::RequiredSymbol, and an error will be generated if any of +/// the requested symbols are not defined. +/// +/// If SymbolLookupFlags::WeaklyReferencedSymbol is used then any missing +/// symbols will have their corresponding address objects set to zero, and +/// this function will never generate an error (the caller will need to check +/// addresses before using them). +/// +/// Asynchronous version. +void lookupAndRecordAddrs( + unique_function OnRecorded, ExecutionSession &ES, LookupKind K, + const JITDylibSearchOrder &SearchOrder, + std::vector> Pairs, + SymbolLookupFlags LookupFlags = SymbolLookupFlags::RequiredSymbol); + +/// Record addresses of the given symbols in the given ExecutorAddrs. +/// +/// Blocking version. +Error lookupAndRecordAddrs( + ExecutionSession &ES, LookupKind K, const JITDylibSearchOrder &SearchOrder, + std::vector> Pairs, + SymbolLookupFlags LookupFlags = SymbolLookupFlags::RequiredSymbol); + +/// Record addresses of given symbols in the given ExecutorAddrs. +/// +/// ExecutorProcessControl lookup version. Lookups are always implicitly +/// weak. +Error lookupAndRecordAddrs( + ExecutorProcessControl &EPC, tpctypes::DylibHandle H, + std::vector> Pairs, + SymbolLookupFlags LookupFlags = SymbolLookupFlags::RequiredSymbol); + +} // End namespace orc +} // End namespace llvm + +#endif // LLVM_EXECUTIONENGINE_ORC_LOOKUPANDRECORDADDRS_H diff --git a/llvm/include/llvm/ExecutionEngine/Orc/MachOPlatform.h b/llvm/include/llvm/ExecutionEngine/Orc/MachOPlatform.h index f77dfd208413..d7b5e2eda6ee 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/MachOPlatform.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/MachOPlatform.h @@ -16,7 +16,6 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ExecutionEngine/Orc/Core.h" #include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h" -#include "llvm/ExecutionEngine/Orc/LLVMSPSSerializers.h" #include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h" #include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h" @@ -27,22 +26,16 @@ namespace llvm { namespace orc { -struct MachOPerObjectSectionsToRegister { - ExecutorAddressRange EHFrameSection; - ExecutorAddressRange ThreadDataSection; -}; - struct MachOJITDylibInitializers { - using SectionList = std::vector; + using SectionList = std::vector; - MachOJITDylibInitializers(std::string Name, - ExecutorAddress MachOHeaderAddress) + MachOJITDylibInitializers(std::string Name, ExecutorAddr MachOHeaderAddress) : Name(std::move(Name)), MachOHeaderAddress(std::move(MachOHeaderAddress)) {} std::string Name; - ExecutorAddress MachOHeaderAddress; - ExecutorAddress ObjCImageInfoAddress; + ExecutorAddr MachOHeaderAddress; + ExecutorAddr ObjCImageInfoAddress; StringMap InitSections; }; @@ -155,15 +148,12 @@ private: using InitSymbolDepMap = DenseMap; - void addInitializerSupportPasses(MaterializationResponsibility &MR, - jitlink::PassConfiguration &Config); - - void addMachOHeaderSupportPasses(MaterializationResponsibility &MR, - jitlink::PassConfiguration &Config); - void addEHAndTLVSupportPasses(MaterializationResponsibility &MR, jitlink::PassConfiguration &Config); + Error associateJITDylibHeaderSymbol(jitlink::LinkGraph &G, + MaterializationResponsibility &MR); + Error preserveInitSections(jitlink::LinkGraph &G, MaterializationResponsibility &MR); @@ -174,6 +164,10 @@ private: Error fixTLVSectionsAndEdges(jitlink::LinkGraph &G, JITDylib &JD); + Error registerEHAndTLVSections(jitlink::LinkGraph &G); + + Error registerEHSectionsPhase1(jitlink::LinkGraph &G); + std::mutex PluginMutex; MachOPlatform &MP; DenseMap> ObjCImageInfos; @@ -186,7 +180,7 @@ private: using SendDeinitializerSequenceFn = unique_function)>; - using SendSymbolAddressFn = unique_function)>; + using SendSymbolAddressFn = unique_function)>; static bool supportedTarget(const Triple &TT); @@ -209,31 +203,34 @@ private: StringRef JDName); void rt_getDeinitializers(SendDeinitializerSequenceFn SendResult, - ExecutorAddress Handle); + ExecutorAddr Handle); - void rt_lookupSymbol(SendSymbolAddressFn SendResult, ExecutorAddress Handle, + void rt_lookupSymbol(SendSymbolAddressFn SendResult, ExecutorAddr Handle, StringRef SymbolName); // Records the addresses of runtime symbols used by the platform. Error bootstrapMachORuntime(JITDylib &PlatformJD); - Error registerInitInfo(JITDylib &JD, ExecutorAddress ObjCImageInfoAddr, + Error registerInitInfo(JITDylib &JD, ExecutorAddr ObjCImageInfoAddr, ArrayRef InitSections); - Error registerPerObjectSections(const MachOPerObjectSectionsToRegister &POSR); - Expected createPThreadKey(); + enum PlatformState { BootstrapPhase1, BootstrapPhase2, Initialized }; + ExecutionSession &ES; ObjectLinkingLayer &ObjLinkingLayer; SymbolStringPtr MachOHeaderStartSymbol; - std::atomic RuntimeBootstrapped{false}; + std::atomic State{BootstrapPhase1}; - ExecutorAddress orc_rt_macho_platform_bootstrap; - ExecutorAddress orc_rt_macho_platform_shutdown; - ExecutorAddress orc_rt_macho_register_object_sections; - ExecutorAddress orc_rt_macho_create_pthread_key; + ExecutorAddr orc_rt_macho_platform_bootstrap; + ExecutorAddr orc_rt_macho_platform_shutdown; + ExecutorAddr orc_rt_macho_register_ehframe_section; + ExecutorAddr orc_rt_macho_deregister_ehframe_section; + ExecutorAddr orc_rt_macho_register_thread_data_section; + ExecutorAddr orc_rt_macho_deregister_thread_data_section; + ExecutorAddr orc_rt_macho_create_pthread_key; DenseMap RegisteredInitSymbols; @@ -241,7 +238,6 @@ private: // aggregating data from the jitlink. std::mutex PlatformMutex; DenseMap InitSeqs; - std::vector BootstrapPOSRs; DenseMap HeaderAddrToJITDylib; DenseMap JITDylibToPThreadKey; @@ -249,38 +245,12 @@ private: namespace shared { -using SPSMachOPerObjectSectionsToRegister = - SPSTuple; - -template <> -class SPSSerializationTraits { - -public: - static size_t size(const MachOPerObjectSectionsToRegister &MOPOSR) { - return SPSMachOPerObjectSectionsToRegister::AsArgList::size( - MOPOSR.EHFrameSection, MOPOSR.ThreadDataSection); - } - - static bool serialize(SPSOutputBuffer &OB, - const MachOPerObjectSectionsToRegister &MOPOSR) { - return SPSMachOPerObjectSectionsToRegister::AsArgList::serialize( - OB, MOPOSR.EHFrameSection, MOPOSR.ThreadDataSection); - } - - static bool deserialize(SPSInputBuffer &IB, - MachOPerObjectSectionsToRegister &MOPOSR) { - return SPSMachOPerObjectSectionsToRegister::AsArgList::deserialize( - IB, MOPOSR.EHFrameSection, MOPOSR.ThreadDataSection); - } -}; - -using SPSNamedExecutorAddressRangeSequenceMap = - SPSSequence>; +using SPSNamedExecutorAddrRangeSequenceMap = + SPSSequence>; using SPSMachOJITDylibInitializers = - SPSTuple; + SPSTuple; using SPSMachOJITDylibInitializerSequence = SPSSequence; diff --git a/llvm/include/llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h b/llvm/include/llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h index 5632118eee4e..109922a46e26 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h @@ -184,13 +184,13 @@ public: } private: - using AllocPtr = std::unique_ptr; + using FinalizedAlloc = jitlink::JITLinkMemoryManager::FinalizedAlloc; void modifyPassConfig(MaterializationResponsibility &MR, jitlink::LinkGraph &G, jitlink::PassConfiguration &PassConfig); void notifyLoaded(MaterializationResponsibility &MR); - Error notifyEmitted(MaterializationResponsibility &MR, AllocPtr Alloc); + Error notifyEmitted(MaterializationResponsibility &MR, FinalizedAlloc FA); Error handleRemoveResources(ResourceKey K) override; void handleTransferResources(ResourceKey DstKey, ResourceKey SrcKey) override; @@ -201,7 +201,7 @@ private: bool OverrideObjectFlags = false; bool AutoClaimObjectSymbols = false; ReturnObjectBufferFunction ReturnObjectBuffer; - DenseMap> Allocs; + DenseMap> Allocs; std::vector> Plugins; }; diff --git a/llvm/include/llvm/ExecutionEngine/Orc/OrcRPCExecutorProcessControl.h b/llvm/include/llvm/ExecutionEngine/Orc/OrcRPCExecutorProcessControl.h deleted file mode 100644 index 4310ba9ce9e0..000000000000 --- a/llvm/include/llvm/ExecutionEngine/Orc/OrcRPCExecutorProcessControl.h +++ /dev/null @@ -1,436 +0,0 @@ -//===-- OrcRPCExecutorProcessControl.h - Remote target control --*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Executor control via ORC RPC. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_EXECUTIONENGINE_ORC_ORCRPCEXECUTORPROCESSCONTROL_H -#define LLVM_EXECUTIONENGINE_ORC_ORCRPCEXECUTORPROCESSCONTROL_H - -#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h" -#include "llvm/ExecutionEngine/Orc/Shared/RPCUtils.h" -#include "llvm/ExecutionEngine/Orc/Shared/RawByteChannel.h" -#include "llvm/ExecutionEngine/Orc/TargetProcess/OrcRPCTPCServer.h" -#include "llvm/Support/MSVCErrorWorkarounds.h" - -namespace llvm { -namespace orc { - -/// JITLinkMemoryManager implementation for a process connected via an ORC RPC -/// endpoint. -template -class OrcRPCEPCJITLinkMemoryManager : public jitlink::JITLinkMemoryManager { -private: - struct HostAlloc { - std::unique_ptr Mem; - uint64_t Size; - }; - - struct TargetAlloc { - JITTargetAddress Address = 0; - uint64_t AllocatedSize = 0; - }; - - using HostAllocMap = DenseMap; - using TargetAllocMap = DenseMap; - -public: - class OrcRPCAllocation : public Allocation { - public: - OrcRPCAllocation(OrcRPCEPCJITLinkMemoryManager &Parent, - HostAllocMap HostAllocs, TargetAllocMap TargetAllocs) - : Parent(Parent), HostAllocs(std::move(HostAllocs)), - TargetAllocs(std::move(TargetAllocs)) { - assert(HostAllocs.size() == TargetAllocs.size() && - "HostAllocs size should match TargetAllocs"); - } - - ~OrcRPCAllocation() override { - assert(TargetAllocs.empty() && "failed to deallocate"); - } - - MutableArrayRef getWorkingMemory(ProtectionFlags Seg) override { - auto I = HostAllocs.find(Seg); - assert(I != HostAllocs.end() && "No host allocation for segment"); - auto &HA = I->second; - return {HA.Mem.get(), static_cast(HA.Size)}; - } - - JITTargetAddress getTargetMemory(ProtectionFlags Seg) override { - auto I = TargetAllocs.find(Seg); - assert(I != TargetAllocs.end() && "No target allocation for segment"); - return I->second.Address; - } - - void finalizeAsync(FinalizeContinuation OnFinalize) override { - - std::vector BufferWrites; - orcrpctpc::ReleaseOrFinalizeMemRequest FMR; - - for (auto &KV : HostAllocs) { - assert(TargetAllocs.count(KV.first) && - "No target allocation for buffer"); - auto &HA = KV.second; - auto &TA = TargetAllocs[KV.first]; - BufferWrites.push_back({TA.Address, StringRef(HA.Mem.get(), HA.Size)}); - FMR.push_back({orcrpctpc::toWireProtectionFlags( - static_cast(KV.first)), - TA.Address, TA.AllocatedSize}); - } - - DEBUG_WITH_TYPE("orc", { - dbgs() << "finalizeAsync " << (void *)this << ":\n"; - auto FMRI = FMR.begin(); - for (auto &B : BufferWrites) { - auto Prot = FMRI->Prot; - ++FMRI; - dbgs() << " Writing " << formatv("{0:x16}", B.Buffer.size()) - << " bytes to " << ((Prot & orcrpctpc::WPF_Read) ? 'R' : '-') - << ((Prot & orcrpctpc::WPF_Write) ? 'W' : '-') - << ((Prot & orcrpctpc::WPF_Exec) ? 'X' : '-') - << " segment: local " << (const void *)B.Buffer.data() - << " -> target " << formatv("{0:x16}", B.Address) << "\n"; - } - }); - if (auto Err = - Parent.Parent.getMemoryAccess().writeBuffers(BufferWrites)) { - OnFinalize(std::move(Err)); - return; - } - - DEBUG_WITH_TYPE("orc", dbgs() << " Applying permissions...\n"); - if (auto Err = - Parent.getEndpoint().template callAsync( - [OF = std::move(OnFinalize)](Error Err2) { - // FIXME: Dispatch to work queue. - std::thread([OF = std::move(OF), - Err3 = std::move(Err2)]() mutable { - DEBUG_WITH_TYPE( - "orc", { dbgs() << " finalizeAsync complete\n"; }); - OF(std::move(Err3)); - }).detach(); - return Error::success(); - }, - FMR)) { - DEBUG_WITH_TYPE("orc", dbgs() << " failed.\n"); - Parent.getEndpoint().abandonPendingResponses(); - Parent.reportError(std::move(Err)); - } - DEBUG_WITH_TYPE("orc", { - dbgs() << "Leaving finalizeAsync (finalization may continue in " - "background)\n"; - }); - } - - Error deallocate() override { - orcrpctpc::ReleaseOrFinalizeMemRequest RMR; - for (auto &KV : TargetAllocs) - RMR.push_back({orcrpctpc::toWireProtectionFlags( - static_cast(KV.first)), - KV.second.Address, KV.second.AllocatedSize}); - TargetAllocs.clear(); - - return Parent.getEndpoint().template callB(RMR); - } - - private: - OrcRPCEPCJITLinkMemoryManager &Parent; - HostAllocMap HostAllocs; - TargetAllocMap TargetAllocs; - }; - - OrcRPCEPCJITLinkMemoryManager(OrcRPCEPCImplT &Parent) : Parent(Parent) {} - - Expected> - allocate(const jitlink::JITLinkDylib *JD, - const SegmentsRequestMap &Request) override { - orcrpctpc::ReserveMemRequest RMR; - HostAllocMap HostAllocs; - - for (auto &KV : Request) { - assert(KV.second.getContentSize() <= std::numeric_limits::max() && - "Content size is out-of-range for host"); - - RMR.push_back({orcrpctpc::toWireProtectionFlags( - static_cast(KV.first)), - KV.second.getContentSize() + KV.second.getZeroFillSize(), - KV.second.getAlignment()}); - HostAllocs[KV.first] = { - std::make_unique(KV.second.getContentSize()), - KV.second.getContentSize()}; - } - - DEBUG_WITH_TYPE("orc", { - dbgs() << "Orc remote memmgr got request:\n"; - for (auto &KV : Request) - dbgs() << " permissions: " - << ((KV.first & sys::Memory::MF_READ) ? 'R' : '-') - << ((KV.first & sys::Memory::MF_WRITE) ? 'W' : '-') - << ((KV.first & sys::Memory::MF_EXEC) ? 'X' : '-') - << ", content size: " - << formatv("{0:x16}", KV.second.getContentSize()) - << " + zero-fill-size: " - << formatv("{0:x16}", KV.second.getZeroFillSize()) - << ", align: " << KV.second.getAlignment() << "\n"; - }); - - // FIXME: LLVM RPC needs to be fixed to support alt - // serialization/deserialization on return types. For now just - // translate from std::map to DenseMap manually. - auto TmpTargetAllocs = - Parent.getEndpoint().template callB(RMR); - if (!TmpTargetAllocs) - return TmpTargetAllocs.takeError(); - - if (TmpTargetAllocs->size() != RMR.size()) - return make_error( - "Number of target allocations does not match request", - inconvertibleErrorCode()); - - TargetAllocMap TargetAllocs; - for (auto &E : *TmpTargetAllocs) - TargetAllocs[orcrpctpc::fromWireProtectionFlags(E.Prot)] = { - E.Address, E.AllocatedSize}; - - DEBUG_WITH_TYPE("orc", { - auto HAI = HostAllocs.begin(); - for (auto &KV : TargetAllocs) - dbgs() << " permissions: " - << ((KV.first & sys::Memory::MF_READ) ? 'R' : '-') - << ((KV.first & sys::Memory::MF_WRITE) ? 'W' : '-') - << ((KV.first & sys::Memory::MF_EXEC) ? 'X' : '-') - << " assigned local " << (void *)HAI->second.Mem.get() - << ", target " << formatv("{0:x16}", KV.second.Address) << "\n"; - }); - - return std::make_unique(*this, std::move(HostAllocs), - std::move(TargetAllocs)); - } - -private: - void reportError(Error Err) { Parent.reportError(std::move(Err)); } - - decltype(std::declval().getEndpoint()) getEndpoint() { - return Parent.getEndpoint(); - } - - OrcRPCEPCImplT &Parent; -}; - -/// ExecutorProcessControl::MemoryAccess implementation for a process connected -/// via an ORC RPC endpoint. -template -class OrcRPCEPCMemoryAccess : public ExecutorProcessControl::MemoryAccess { -public: - OrcRPCEPCMemoryAccess(OrcRPCEPCImplT &Parent) : Parent(Parent) {} - - void writeUInt8s(ArrayRef Ws, - WriteResultFn OnWriteComplete) override { - writeViaRPC(Ws, std::move(OnWriteComplete)); - } - - void writeUInt16s(ArrayRef Ws, - WriteResultFn OnWriteComplete) override { - writeViaRPC(Ws, std::move(OnWriteComplete)); - } - - void writeUInt32s(ArrayRef Ws, - WriteResultFn OnWriteComplete) override { - writeViaRPC(Ws, std::move(OnWriteComplete)); - } - - void writeUInt64s(ArrayRef Ws, - WriteResultFn OnWriteComplete) override { - writeViaRPC(Ws, std::move(OnWriteComplete)); - } - - void writeBuffers(ArrayRef Ws, - WriteResultFn OnWriteComplete) override { - writeViaRPC(Ws, std::move(OnWriteComplete)); - } - -private: - template - void writeViaRPC(ArrayRef Ws, WriteResultFn OnWriteComplete) { - if (auto Err = Parent.getEndpoint().template callAsync( - [OWC = std::move(OnWriteComplete)](Error Err2) mutable -> Error { - OWC(std::move(Err2)); - return Error::success(); - }, - Ws)) { - Parent.reportError(std::move(Err)); - Parent.getEndpoint().abandonPendingResponses(); - } - } - - OrcRPCEPCImplT &Parent; -}; - -// ExecutorProcessControl for a process connected via an ORC RPC Endpoint. -template -class OrcRPCExecutorProcessControlBase : public ExecutorProcessControl { -public: - using ErrorReporter = unique_function; - - using OnCloseConnectionFunction = unique_function; - - OrcRPCExecutorProcessControlBase(std::shared_ptr SSP, - RPCEndpointT &EP, ErrorReporter ReportError) - : ExecutorProcessControl(std::move(SSP)), - ReportError(std::move(ReportError)), EP(EP) { - using ThisT = OrcRPCExecutorProcessControlBase; - EP.template addAsyncHandler(*this, - &ThisT::runWrapperInJIT); - } - - void reportError(Error Err) { ReportError(std::move(Err)); } - - RPCEndpointT &getEndpoint() { return EP; } - - Expected loadDylib(const char *DylibPath) override { - DEBUG_WITH_TYPE("orc", { - dbgs() << "Loading dylib \"" << (DylibPath ? DylibPath : "") << "\" "; - if (!DylibPath) - dbgs() << "(process symbols)"; - dbgs() << "\n"; - }); - if (!DylibPath) - DylibPath = ""; - auto H = EP.template callB(DylibPath); - DEBUG_WITH_TYPE("orc", { - if (H) - dbgs() << " got handle " << formatv("{0:x16}", *H) << "\n"; - else - dbgs() << " error, unable to load\n"; - }); - return H; - } - - Expected> - lookupSymbols(ArrayRef Request) override { - std::vector RR; - for (auto &E : Request) { - RR.push_back({}); - RR.back().first = E.Handle; - for (auto &KV : E.Symbols) - RR.back().second.push_back( - {(*KV.first).str(), - KV.second == SymbolLookupFlags::WeaklyReferencedSymbol}); - } - DEBUG_WITH_TYPE("orc", { - dbgs() << "Compound lookup:\n"; - for (auto &R : Request) { - dbgs() << " In " << formatv("{0:x16}", R.Handle) << ": {"; - bool First = true; - for (auto &KV : R.Symbols) { - dbgs() << (First ? "" : ",") << " " << *KV.first; - First = false; - } - dbgs() << " }\n"; - } - }); - return EP.template callB(RR); - } - - Expected runAsMain(JITTargetAddress MainFnAddr, - ArrayRef Args) override { - DEBUG_WITH_TYPE("orc", { - dbgs() << "Running as main: " << formatv("{0:x16}", MainFnAddr) - << ", args = ["; - for (unsigned I = 0; I != Args.size(); ++I) - dbgs() << (I ? "," : "") << " \"" << Args[I] << "\""; - dbgs() << "]\n"; - }); - auto Result = EP.template callB(MainFnAddr, Args); - DEBUG_WITH_TYPE("orc", { - dbgs() << " call to " << formatv("{0:x16}", MainFnAddr); - if (Result) - dbgs() << " returned result " << *Result << "\n"; - else - dbgs() << " failed\n"; - }); - return Result; - } - - void callWrapperAsync(SendResultFunction OnComplete, - JITTargetAddress WrapperFnAddr, - ArrayRef ArgBuffer) override { - DEBUG_WITH_TYPE("orc", { - dbgs() << "Running as wrapper function " - << formatv("{0:x16}", WrapperFnAddr) << " with " - << formatv("{0:x16}", ArgBuffer.size()) << " argument buffer\n"; - }); - auto Result = EP.template callB( - WrapperFnAddr, - ArrayRef(reinterpret_cast(ArgBuffer.data()), - ArgBuffer.size())); - - if (!Result) - OnComplete(shared::WrapperFunctionResult::createOutOfBandError( - toString(Result.takeError()))); - OnComplete(std::move(*Result)); - } - - Error closeConnection(OnCloseConnectionFunction OnCloseConnection) { - DEBUG_WITH_TYPE("orc", dbgs() << "Closing connection to remote\n"); - return EP.template callAsync( - std::move(OnCloseConnection)); - } - - Error closeConnectionAndWait() { - std::promise P; - auto F = P.get_future(); - if (auto Err = closeConnection([&](Error Err2) -> Error { - P.set_value(std::move(Err2)); - return Error::success(); - })) { - EP.abandonAllPendingResponses(); - return joinErrors(std::move(Err), F.get()); - } - return F.get(); - } - -protected: - /// Subclasses must call this during construction to initialize the - /// TargetTriple and PageSize members. - Error initializeORCRPCEPCBase() { - if (auto EPI = EP.template callB()) { - this->TargetTriple = Triple(EPI->Triple); - this->PageSize = PageSize; - this->JDI = {ExecutorAddress(EPI->DispatchFuncAddr), - ExecutorAddress(EPI->DispatchCtxAddr)}; - return Error::success(); - } else - return EPI.takeError(); - } - -private: - Error runWrapperInJIT( - std::function)> SendResult, - JITTargetAddress FunctionTag, std::vector ArgBuffer) { - - getExecutionSession().runJITDispatchHandler( - [this, SendResult = std::move(SendResult)]( - Expected R) { - if (auto Err = SendResult(std::move(R))) - ReportError(std::move(Err)); - }, - FunctionTag, - {reinterpret_cast(ArgBuffer.data()), ArgBuffer.size()}); - return Error::success(); - } - - ErrorReporter ReportError; - RPCEndpointT &EP; -}; - -} // end namespace orc -} // end namespace llvm - -#endif // LLVM_EXECUTIONENGINE_ORC_ORCRPCEXECUTORPROCESSCONTROL_H diff --git a/llvm/include/llvm/ExecutionEngine/Orc/OrcRemoteTargetClient.h b/llvm/include/llvm/ExecutionEngine/Orc/OrcRemoteTargetClient.h deleted file mode 100644 index 3d139740d677..000000000000 --- a/llvm/include/llvm/ExecutionEngine/Orc/OrcRemoteTargetClient.h +++ /dev/null @@ -1,925 +0,0 @@ -//===- OrcRemoteTargetClient.h - Orc Remote-target Client -------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file defines the OrcRemoteTargetClient class and helpers. This class -// can be used to communicate over an RawByteChannel with an -// OrcRemoteTargetServer instance to support remote-JITing. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_EXECUTIONENGINE_ORC_ORCREMOTETARGETCLIENT_H -#define LLVM_EXECUTIONENGINE_ORC_ORCREMOTETARGETCLIENT_H - -#include "llvm/ADT/Optional.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/StringMap.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ExecutionEngine/JITSymbol.h" -#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h" -#include "llvm/ExecutionEngine/Orc/IndirectionUtils.h" -#include "llvm/ExecutionEngine/Orc/OrcRemoteTargetRPCAPI.h" -#include "llvm/ExecutionEngine/RuntimeDyld.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/Error.h" -#include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/Format.h" -#include "llvm/Support/MathExtras.h" -#include "llvm/Support/Memory.h" -#include "llvm/Support/raw_ostream.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#define DEBUG_TYPE "orc-remote" - -namespace llvm { -namespace orc { -namespace remote { - -/// This class provides utilities (including memory manager, indirect stubs -/// manager, and compile callback manager types) that support remote JITing -/// in ORC. -/// -/// Each of the utility classes talks to a JIT server (an instance of the -/// OrcRemoteTargetServer class) via an RPC system (see RPCUtils.h) to carry out -/// its actions. -class OrcRemoteTargetClient - : public shared::SingleThreadedRPCEndpoint { -public: - /// Remote-mapped RuntimeDyld-compatible memory manager. - class RemoteRTDyldMemoryManager : public RuntimeDyld::MemoryManager { - friend class OrcRemoteTargetClient; - - public: - ~RemoteRTDyldMemoryManager() { - Client.destroyRemoteAllocator(Id); - LLVM_DEBUG(dbgs() << "Destroyed remote allocator " << Id << "\n"); - } - - RemoteRTDyldMemoryManager(const RemoteRTDyldMemoryManager &) = delete; - RemoteRTDyldMemoryManager & - operator=(const RemoteRTDyldMemoryManager &) = delete; - RemoteRTDyldMemoryManager(RemoteRTDyldMemoryManager &&) = default; - RemoteRTDyldMemoryManager &operator=(RemoteRTDyldMemoryManager &&) = delete; - - uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, - unsigned SectionID, - StringRef SectionName) override { - Unmapped.back().CodeAllocs.emplace_back(Size, Alignment); - uint8_t *Alloc = reinterpret_cast( - Unmapped.back().CodeAllocs.back().getLocalAddress()); - LLVM_DEBUG(dbgs() << "Allocator " << Id << " allocated code for " - << SectionName << ": " << Alloc << " (" << Size - << " bytes, alignment " << Alignment << ")\n"); - return Alloc; - } - - uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, - unsigned SectionID, StringRef SectionName, - bool IsReadOnly) override { - if (IsReadOnly) { - Unmapped.back().RODataAllocs.emplace_back(Size, Alignment); - uint8_t *Alloc = reinterpret_cast( - Unmapped.back().RODataAllocs.back().getLocalAddress()); - LLVM_DEBUG(dbgs() << "Allocator " << Id << " allocated ro-data for " - << SectionName << ": " << Alloc << " (" << Size - << " bytes, alignment " << Alignment << ")\n"); - return Alloc; - } // else... - - Unmapped.back().RWDataAllocs.emplace_back(Size, Alignment); - uint8_t *Alloc = reinterpret_cast( - Unmapped.back().RWDataAllocs.back().getLocalAddress()); - LLVM_DEBUG(dbgs() << "Allocator " << Id << " allocated rw-data for " - << SectionName << ": " << Alloc << " (" << Size - << " bytes, alignment " << Alignment << ")\n"); - return Alloc; - } - - void reserveAllocationSpace(uintptr_t CodeSize, uint32_t CodeAlign, - uintptr_t RODataSize, uint32_t RODataAlign, - uintptr_t RWDataSize, - uint32_t RWDataAlign) override { - Unmapped.push_back(ObjectAllocs()); - - LLVM_DEBUG(dbgs() << "Allocator " << Id << " reserved:\n"); - - if (CodeSize != 0) { - Unmapped.back().RemoteCodeAddr = - Client.reserveMem(Id, CodeSize, CodeAlign); - - LLVM_DEBUG( - dbgs() << " code: " - << format("0x%016" PRIx64, Unmapped.back().RemoteCodeAddr) - << " (" << CodeSize << " bytes, alignment " << CodeAlign - << ")\n"); - } - - if (RODataSize != 0) { - Unmapped.back().RemoteRODataAddr = - Client.reserveMem(Id, RODataSize, RODataAlign); - - LLVM_DEBUG( - dbgs() << " ro-data: " - << format("0x%016" PRIx64, Unmapped.back().RemoteRODataAddr) - << " (" << RODataSize << " bytes, alignment " << RODataAlign - << ")\n"); - } - - if (RWDataSize != 0) { - Unmapped.back().RemoteRWDataAddr = - Client.reserveMem(Id, RWDataSize, RWDataAlign); - - LLVM_DEBUG( - dbgs() << " rw-data: " - << format("0x%016" PRIx64, Unmapped.back().RemoteRWDataAddr) - << " (" << RWDataSize << " bytes, alignment " << RWDataAlign - << ")\n"); - } - } - - bool needsToReserveAllocationSpace() override { return true; } - - void registerEHFrames(uint8_t *Addr, uint64_t LoadAddr, - size_t Size) override { - UnfinalizedEHFrames.push_back({LoadAddr, Size}); - } - - void deregisterEHFrames() override { - for (auto &Frame : RegisteredEHFrames) { - // FIXME: Add error poll. - Client.deregisterEHFrames(Frame.Addr, Frame.Size); - } - } - - void notifyObjectLoaded(RuntimeDyld &Dyld, - const object::ObjectFile &Obj) override { - LLVM_DEBUG(dbgs() << "Allocator " << Id << " applied mappings:\n"); - for (auto &ObjAllocs : Unmapped) { - mapAllocsToRemoteAddrs(Dyld, ObjAllocs.CodeAllocs, - ObjAllocs.RemoteCodeAddr); - mapAllocsToRemoteAddrs(Dyld, ObjAllocs.RODataAllocs, - ObjAllocs.RemoteRODataAddr); - mapAllocsToRemoteAddrs(Dyld, ObjAllocs.RWDataAllocs, - ObjAllocs.RemoteRWDataAddr); - Unfinalized.push_back(std::move(ObjAllocs)); - } - Unmapped.clear(); - } - - bool finalizeMemory(std::string *ErrMsg = nullptr) override { - LLVM_DEBUG(dbgs() << "Allocator " << Id << " finalizing:\n"); - - for (auto &ObjAllocs : Unfinalized) { - if (copyAndProtect(ObjAllocs.CodeAllocs, ObjAllocs.RemoteCodeAddr, - sys::Memory::MF_READ | sys::Memory::MF_EXEC)) - return true; - - if (copyAndProtect(ObjAllocs.RODataAllocs, ObjAllocs.RemoteRODataAddr, - sys::Memory::MF_READ)) - return true; - - if (copyAndProtect(ObjAllocs.RWDataAllocs, ObjAllocs.RemoteRWDataAddr, - sys::Memory::MF_READ | sys::Memory::MF_WRITE)) - return true; - } - Unfinalized.clear(); - - for (auto &EHFrame : UnfinalizedEHFrames) { - if (auto Err = Client.registerEHFrames(EHFrame.Addr, EHFrame.Size)) { - // FIXME: Replace this once finalizeMemory can return an Error. - handleAllErrors(std::move(Err), [&](ErrorInfoBase &EIB) { - if (ErrMsg) { - raw_string_ostream ErrOut(*ErrMsg); - EIB.log(ErrOut); - } - }); - return false; - } - } - RegisteredEHFrames = std::move(UnfinalizedEHFrames); - UnfinalizedEHFrames = {}; - - return false; - } - - private: - class Alloc { - public: - Alloc(uint64_t Size, unsigned Align) - : Size(Size), Align(Align), Contents(new char[Size + Align - 1]) {} - - Alloc(const Alloc &) = delete; - Alloc &operator=(const Alloc &) = delete; - Alloc(Alloc &&) = default; - Alloc &operator=(Alloc &&) = default; - - uint64_t getSize() const { return Size; } - - unsigned getAlign() const { return Align; } - - char *getLocalAddress() const { - uintptr_t LocalAddr = reinterpret_cast(Contents.get()); - LocalAddr = alignTo(LocalAddr, Align); - return reinterpret_cast(LocalAddr); - } - - void setRemoteAddress(JITTargetAddress RemoteAddr) { - this->RemoteAddr = RemoteAddr; - } - - JITTargetAddress getRemoteAddress() const { return RemoteAddr; } - - private: - uint64_t Size; - unsigned Align; - std::unique_ptr Contents; - JITTargetAddress RemoteAddr = 0; - }; - - struct ObjectAllocs { - ObjectAllocs() = default; - ObjectAllocs(const ObjectAllocs &) = delete; - ObjectAllocs &operator=(const ObjectAllocs &) = delete; - ObjectAllocs(ObjectAllocs &&) = default; - ObjectAllocs &operator=(ObjectAllocs &&) = default; - - JITTargetAddress RemoteCodeAddr = 0; - JITTargetAddress RemoteRODataAddr = 0; - JITTargetAddress RemoteRWDataAddr = 0; - std::vector CodeAllocs, RODataAllocs, RWDataAllocs; - }; - - RemoteRTDyldMemoryManager(OrcRemoteTargetClient &Client, - ResourceIdMgr::ResourceId Id) - : Client(Client), Id(Id) { - LLVM_DEBUG(dbgs() << "Created remote allocator " << Id << "\n"); - } - - // Maps all allocations in Allocs to aligned blocks - void mapAllocsToRemoteAddrs(RuntimeDyld &Dyld, std::vector &Allocs, - JITTargetAddress NextAddr) { - for (auto &Alloc : Allocs) { - NextAddr = alignTo(NextAddr, Alloc.getAlign()); - Dyld.mapSectionAddress(Alloc.getLocalAddress(), NextAddr); - LLVM_DEBUG( - dbgs() << " " << static_cast(Alloc.getLocalAddress()) - << " -> " << format("0x%016" PRIx64, NextAddr) << "\n"); - Alloc.setRemoteAddress(NextAddr); - - // Only advance NextAddr if it was non-null to begin with, - // otherwise leave it as null. - if (NextAddr) - NextAddr += Alloc.getSize(); - } - } - - // Copies data for each alloc in the list, then set permissions on the - // segment. - bool copyAndProtect(const std::vector &Allocs, - JITTargetAddress RemoteSegmentAddr, - unsigned Permissions) { - if (RemoteSegmentAddr) { - assert(!Allocs.empty() && "No sections in allocated segment"); - - for (auto &Alloc : Allocs) { - LLVM_DEBUG(dbgs() << " copying section: " - << static_cast(Alloc.getLocalAddress()) - << " -> " - << format("0x%016" PRIx64, Alloc.getRemoteAddress()) - << " (" << Alloc.getSize() << " bytes)\n";); - - if (Client.writeMem(Alloc.getRemoteAddress(), Alloc.getLocalAddress(), - Alloc.getSize())) - return true; - } - - LLVM_DEBUG(dbgs() << " setting " - << (Permissions & sys::Memory::MF_READ ? 'R' : '-') - << (Permissions & sys::Memory::MF_WRITE ? 'W' : '-') - << (Permissions & sys::Memory::MF_EXEC ? 'X' : '-') - << " permissions on block: " - << format("0x%016" PRIx64, RemoteSegmentAddr) - << "\n"); - if (Client.setProtections(Id, RemoteSegmentAddr, Permissions)) - return true; - } - return false; - } - - OrcRemoteTargetClient &Client; - ResourceIdMgr::ResourceId Id; - std::vector Unmapped; - std::vector Unfinalized; - - struct EHFrame { - JITTargetAddress Addr; - uint64_t Size; - }; - std::vector UnfinalizedEHFrames; - std::vector RegisteredEHFrames; - }; - - class RPCMMAlloc : public jitlink::JITLinkMemoryManager::Allocation { - using AllocationMap = DenseMap; - using FinalizeContinuation = - jitlink::JITLinkMemoryManager::Allocation::FinalizeContinuation; - using ProtectionFlags = sys::Memory::ProtectionFlags; - using SegmentsRequestMap = - DenseMap; - - RPCMMAlloc(OrcRemoteTargetClient &Client, ResourceIdMgr::ResourceId Id) - : Client(Client), Id(Id) {} - - public: - static Expected> - Create(OrcRemoteTargetClient &Client, ResourceIdMgr::ResourceId Id, - const SegmentsRequestMap &Request) { - auto *MM = new RPCMMAlloc(Client, Id); - - if (Error Err = MM->allocateHostBlocks(Request)) - return std::move(Err); - - if (Error Err = MM->allocateTargetBlocks()) - return std::move(Err); - - return std::unique_ptr(MM); - } - - MutableArrayRef getWorkingMemory(ProtectionFlags Seg) override { - assert(HostSegBlocks.count(Seg) && "No allocation for segment"); - return {static_cast(HostSegBlocks[Seg].base()), - HostSegBlocks[Seg].allocatedSize()}; - } - - JITTargetAddress getTargetMemory(ProtectionFlags Seg) override { - assert(TargetSegBlocks.count(Seg) && "No allocation for segment"); - return pointerToJITTargetAddress(TargetSegBlocks[Seg].base()); - } - - void finalizeAsync(FinalizeContinuation OnFinalize) override { - // Host allocations (working memory) remain ReadWrite. - OnFinalize(copyAndProtect()); - } - - Error deallocate() override { - // TODO: Cannot release target allocation. RPCAPI has no function - // symmetric to reserveMem(). Add RPC call like freeMem()? - return errorCodeToError(sys::Memory::releaseMappedMemory(HostAllocation)); - } - - private: - OrcRemoteTargetClient &Client; - ResourceIdMgr::ResourceId Id; - AllocationMap HostSegBlocks; - AllocationMap TargetSegBlocks; - JITTargetAddress TargetSegmentAddr; - sys::MemoryBlock HostAllocation; - - Error allocateHostBlocks(const SegmentsRequestMap &Request) { - unsigned TargetPageSize = Client.getPageSize(); - - if (!isPowerOf2_64(static_cast(TargetPageSize))) - return make_error("Host page size is not a power of 2", - inconvertibleErrorCode()); - - auto TotalSize = calcTotalAllocSize(Request, TargetPageSize); - if (!TotalSize) - return TotalSize.takeError(); - - // Allocate one slab to cover all the segments. - const sys::Memory::ProtectionFlags ReadWrite = - static_cast(sys::Memory::MF_READ | - sys::Memory::MF_WRITE); - std::error_code EC; - HostAllocation = - sys::Memory::allocateMappedMemory(*TotalSize, nullptr, ReadWrite, EC); - if (EC) - return errorCodeToError(EC); - - char *SlabAddr = static_cast(HostAllocation.base()); -#ifndef NDEBUG - char *SlabAddrEnd = SlabAddr + HostAllocation.allocatedSize(); -#endif - - // Allocate segment memory from the slab. - for (auto &KV : Request) { - const auto &Seg = KV.second; - - uint64_t SegmentSize = Seg.getContentSize() + Seg.getZeroFillSize(); - uint64_t AlignedSegmentSize = alignTo(SegmentSize, TargetPageSize); - - // Zero out zero-fill memory. - char *ZeroFillBegin = SlabAddr + Seg.getContentSize(); - memset(ZeroFillBegin, 0, Seg.getZeroFillSize()); - - // Record the block for this segment. - HostSegBlocks[KV.first] = - sys::MemoryBlock(SlabAddr, AlignedSegmentSize); - - SlabAddr += AlignedSegmentSize; - assert(SlabAddr <= SlabAddrEnd && "Out of range"); - } - - return Error::success(); - } - - Error allocateTargetBlocks() { - // Reserve memory for all blocks on the target. We need as much space on - // the target as we allocated on the host. - TargetSegmentAddr = Client.reserveMem(Id, HostAllocation.allocatedSize(), - Client.getPageSize()); - if (!TargetSegmentAddr) - return make_error("Failed to reserve memory on the target", - inconvertibleErrorCode()); - - // Map memory blocks into the allocation, that match the host allocation. - JITTargetAddress TargetAllocAddr = TargetSegmentAddr; - for (const auto &KV : HostSegBlocks) { - size_t TargetAllocSize = KV.second.allocatedSize(); - - TargetSegBlocks[KV.first] = - sys::MemoryBlock(jitTargetAddressToPointer(TargetAllocAddr), - TargetAllocSize); - - TargetAllocAddr += TargetAllocSize; - assert(TargetAllocAddr - TargetSegmentAddr <= - HostAllocation.allocatedSize() && - "Out of range on target"); - } - - return Error::success(); - } - - Error copyAndProtect() { - unsigned Permissions = 0u; - - // Copy segments one by one. - for (auto &KV : TargetSegBlocks) { - Permissions |= KV.first; - - const sys::MemoryBlock &TargetBlock = KV.second; - const sys::MemoryBlock &HostBlock = HostSegBlocks.lookup(KV.first); - - size_t TargetAllocSize = TargetBlock.allocatedSize(); - auto TargetAllocAddr = pointerToJITTargetAddress(TargetBlock.base()); - auto *HostAllocBegin = static_cast(HostBlock.base()); - - bool CopyErr = - Client.writeMem(TargetAllocAddr, HostAllocBegin, TargetAllocSize); - if (CopyErr) - return createStringError(inconvertibleErrorCode(), - "Failed to copy %d segment to the target", - KV.first); - } - - // Set permission flags for all segments at once. - bool ProtectErr = - Client.setProtections(Id, TargetSegmentAddr, Permissions); - if (ProtectErr) - return createStringError(inconvertibleErrorCode(), - "Failed to apply permissions for %d segment " - "on the target", - Permissions); - return Error::success(); - } - - static Expected - calcTotalAllocSize(const SegmentsRequestMap &Request, - unsigned TargetPageSize) { - size_t TotalSize = 0; - for (const auto &KV : Request) { - const auto &Seg = KV.second; - - if (Seg.getAlignment() > TargetPageSize) - return make_error("Cannot request alignment higher than " - "page alignment on target", - inconvertibleErrorCode()); - - TotalSize = alignTo(TotalSize, TargetPageSize); - TotalSize += Seg.getContentSize(); - TotalSize += Seg.getZeroFillSize(); - } - - return TotalSize; - } - }; - - class RemoteJITLinkMemoryManager : public jitlink::JITLinkMemoryManager { - public: - RemoteJITLinkMemoryManager(OrcRemoteTargetClient &Client, - ResourceIdMgr::ResourceId Id) - : Client(Client), Id(Id) {} - - RemoteJITLinkMemoryManager(const RemoteJITLinkMemoryManager &) = delete; - RemoteJITLinkMemoryManager(RemoteJITLinkMemoryManager &&) = default; - - RemoteJITLinkMemoryManager & - operator=(const RemoteJITLinkMemoryManager &) = delete; - RemoteJITLinkMemoryManager & - operator=(RemoteJITLinkMemoryManager &&) = delete; - - ~RemoteJITLinkMemoryManager() { - Client.destroyRemoteAllocator(Id); - LLVM_DEBUG(dbgs() << "Destroyed remote allocator " << Id << "\n"); - } - - Expected> - allocate(const jitlink::JITLinkDylib *JD, - const SegmentsRequestMap &Request) override { - return RPCMMAlloc::Create(Client, Id, Request); - } - - private: - OrcRemoteTargetClient &Client; - ResourceIdMgr::ResourceId Id; - }; - - /// Remote indirect stubs manager. - class RemoteIndirectStubsManager : public IndirectStubsManager { - public: - RemoteIndirectStubsManager(OrcRemoteTargetClient &Client, - ResourceIdMgr::ResourceId Id) - : Client(Client), Id(Id) {} - - ~RemoteIndirectStubsManager() override { - Client.destroyIndirectStubsManager(Id); - } - - Error createStub(StringRef StubName, JITTargetAddress StubAddr, - JITSymbolFlags StubFlags) override { - if (auto Err = reserveStubs(1)) - return Err; - - return createStubInternal(StubName, StubAddr, StubFlags); - } - - Error createStubs(const StubInitsMap &StubInits) override { - if (auto Err = reserveStubs(StubInits.size())) - return Err; - - for (auto &Entry : StubInits) - if (auto Err = createStubInternal(Entry.first(), Entry.second.first, - Entry.second.second)) - return Err; - - return Error::success(); - } - - JITEvaluatedSymbol findStub(StringRef Name, bool ExportedStubsOnly) override { - auto I = StubIndexes.find(Name); - if (I == StubIndexes.end()) - return nullptr; - auto Key = I->second.first; - auto Flags = I->second.second; - auto StubSymbol = JITEvaluatedSymbol(getStubAddr(Key), Flags); - if (ExportedStubsOnly && !StubSymbol.getFlags().isExported()) - return nullptr; - return StubSymbol; - } - - JITEvaluatedSymbol findPointer(StringRef Name) override { - auto I = StubIndexes.find(Name); - if (I == StubIndexes.end()) - return nullptr; - auto Key = I->second.first; - auto Flags = I->second.second; - return JITEvaluatedSymbol(getPtrAddr(Key), Flags); - } - - Error updatePointer(StringRef Name, JITTargetAddress NewAddr) override { - auto I = StubIndexes.find(Name); - assert(I != StubIndexes.end() && "No stub pointer for symbol"); - auto Key = I->second.first; - return Client.writePointer(getPtrAddr(Key), NewAddr); - } - - private: - struct RemoteIndirectStubsInfo { - JITTargetAddress StubBase; - JITTargetAddress PtrBase; - unsigned NumStubs; - }; - - using StubKey = std::pair; - - Error reserveStubs(unsigned NumStubs) { - if (NumStubs <= FreeStubs.size()) - return Error::success(); - - unsigned NewStubsRequired = NumStubs - FreeStubs.size(); - JITTargetAddress StubBase; - JITTargetAddress PtrBase; - unsigned NumStubsEmitted; - - if (auto StubInfoOrErr = Client.emitIndirectStubs(Id, NewStubsRequired)) - std::tie(StubBase, PtrBase, NumStubsEmitted) = *StubInfoOrErr; - else - return StubInfoOrErr.takeError(); - - unsigned NewBlockId = RemoteIndirectStubsInfos.size(); - RemoteIndirectStubsInfos.push_back({StubBase, PtrBase, NumStubsEmitted}); - - for (unsigned I = 0; I < NumStubsEmitted; ++I) - FreeStubs.push_back(std::make_pair(NewBlockId, I)); - - return Error::success(); - } - - Error createStubInternal(StringRef StubName, JITTargetAddress InitAddr, - JITSymbolFlags StubFlags) { - auto Key = FreeStubs.back(); - FreeStubs.pop_back(); - StubIndexes[StubName] = std::make_pair(Key, StubFlags); - return Client.writePointer(getPtrAddr(Key), InitAddr); - } - - JITTargetAddress getStubAddr(StubKey K) { - assert(RemoteIndirectStubsInfos[K.first].StubBase != 0 && - "Missing stub address"); - return RemoteIndirectStubsInfos[K.first].StubBase + - K.second * Client.getIndirectStubSize(); - } - - JITTargetAddress getPtrAddr(StubKey K) { - assert(RemoteIndirectStubsInfos[K.first].PtrBase != 0 && - "Missing pointer address"); - return RemoteIndirectStubsInfos[K.first].PtrBase + - K.second * Client.getPointerSize(); - } - - OrcRemoteTargetClient &Client; - ResourceIdMgr::ResourceId Id; - std::vector RemoteIndirectStubsInfos; - std::vector FreeStubs; - StringMap> StubIndexes; - }; - - class RemoteTrampolinePool : public TrampolinePool { - public: - RemoteTrampolinePool(OrcRemoteTargetClient &Client) : Client(Client) {} - - private: - Error grow() override { - JITTargetAddress BlockAddr = 0; - uint32_t NumTrampolines = 0; - if (auto TrampolineInfoOrErr = Client.emitTrampolineBlock()) - std::tie(BlockAddr, NumTrampolines) = *TrampolineInfoOrErr; - else - return TrampolineInfoOrErr.takeError(); - - uint32_t TrampolineSize = Client.getTrampolineSize(); - for (unsigned I = 0; I < NumTrampolines; ++I) - AvailableTrampolines.push_back(BlockAddr + (I * TrampolineSize)); - - return Error::success(); - } - - OrcRemoteTargetClient &Client; - }; - - /// Remote compile callback manager. - class RemoteCompileCallbackManager : public JITCompileCallbackManager { - public: - RemoteCompileCallbackManager(OrcRemoteTargetClient &Client, - ExecutionSession &ES, - JITTargetAddress ErrorHandlerAddress) - : JITCompileCallbackManager( - std::make_unique(Client), ES, - ErrorHandlerAddress) {} - }; - - /// Create an OrcRemoteTargetClient. - /// Channel is the ChannelT instance to communicate on. It is assumed that - /// the channel is ready to be read from and written to. - static Expected> - Create(shared::RawByteChannel &Channel, ExecutionSession &ES) { - Error Err = Error::success(); - auto Client = std::unique_ptr( - new OrcRemoteTargetClient(Channel, ES, Err)); - if (Err) - return std::move(Err); - return std::move(Client); - } - - /// Call the int(void) function at the given address in the target and return - /// its result. - Expected callIntVoid(JITTargetAddress Addr) { - LLVM_DEBUG(dbgs() << "Calling int(*)(void) " - << format("0x%016" PRIx64, Addr) << "\n"); - return callB(Addr); - } - - /// Call the int(int) function at the given address in the target and return - /// its result. - Expected callIntInt(JITTargetAddress Addr, int Arg) { - LLVM_DEBUG(dbgs() << "Calling int(*)(int) " << format("0x%016" PRIx64, Addr) - << "\n"); - return callB(Addr, Arg); - } - - /// Call the int(int, char*[]) function at the given address in the target and - /// return its result. - Expected callMain(JITTargetAddress Addr, - const std::vector &Args) { - LLVM_DEBUG(dbgs() << "Calling int(*)(int, char*[]) " - << format("0x%016" PRIx64, Addr) << "\n"); - return callB(Addr, Args); - } - - /// Call the void() function at the given address in the target and wait for - /// it to finish. - Error callVoidVoid(JITTargetAddress Addr) { - LLVM_DEBUG(dbgs() << "Calling void(*)(void) " - << format("0x%016" PRIx64, Addr) << "\n"); - return callB(Addr); - } - - /// Create an RCMemoryManager which will allocate its memory on the remote - /// target. - Expected> - createRemoteMemoryManager() { - auto Id = AllocatorIds.getNext(); - if (auto Err = callB(Id)) - return std::move(Err); - return std::unique_ptr( - new RemoteRTDyldMemoryManager(*this, Id)); - } - - /// Create a JITLink-compatible memory manager which will allocate working - /// memory on the host and target memory on the remote target. - Expected> - createRemoteJITLinkMemoryManager() { - auto Id = AllocatorIds.getNext(); - if (auto Err = callB(Id)) - return std::move(Err); - LLVM_DEBUG(dbgs() << "Created remote allocator " << Id << "\n"); - return std::unique_ptr( - new RemoteJITLinkMemoryManager(*this, Id)); - } - - /// Create an RCIndirectStubsManager that will allocate stubs on the remote - /// target. - Expected> - createIndirectStubsManager() { - auto Id = IndirectStubOwnerIds.getNext(); - if (auto Err = callB(Id)) - return std::move(Err); - return std::make_unique(*this, Id); - } - - Expected - enableCompileCallbacks(JITTargetAddress ErrorHandlerAddress) { - assert(!CallbackManager && "CallbackManager already obtained"); - - // Emit the resolver block on the JIT server. - if (auto Err = callB()) - return std::move(Err); - - // Create the callback manager. - CallbackManager.emplace(*this, ES, ErrorHandlerAddress); - RemoteCompileCallbackManager &Mgr = *CallbackManager; - return Mgr; - } - - /// Search for symbols in the remote process. Note: This should be used by - /// symbol resolvers *after* they've searched the local symbol table in the - /// JIT stack. - Expected getSymbolAddress(StringRef Name) { - return callB(Name); - } - - /// Get the triple for the remote target. - const std::string &getTargetTriple() const { return RemoteTargetTriple; } - - Error terminateSession() { return callB(); } - -private: - OrcRemoteTargetClient(shared::RawByteChannel &Channel, ExecutionSession &ES, - Error &Err) - : shared::SingleThreadedRPCEndpoint(Channel, - true), - ES(ES) { - ErrorAsOutParameter EAO(&Err); - - addHandler( - [this](JITTargetAddress Addr) -> JITTargetAddress { - if (CallbackManager) - return CallbackManager->executeCompileCallback(Addr); - return 0; - }); - - if (auto RIOrErr = callB()) { - std::tie(RemoteTargetTriple, RemotePointerSize, RemotePageSize, - RemoteTrampolineSize, RemoteIndirectStubSize) = *RIOrErr; - Err = Error::success(); - } else - Err = RIOrErr.takeError(); - } - - void deregisterEHFrames(JITTargetAddress Addr, uint32_t Size) { - if (auto Err = callB(Addr, Size)) - ES.reportError(std::move(Err)); - } - - void destroyRemoteAllocator(ResourceIdMgr::ResourceId Id) { - if (auto Err = callB(Id)) { - // FIXME: This will be triggered by a removeModuleSet call: Propagate - // error return up through that. - llvm_unreachable("Failed to destroy remote allocator."); - AllocatorIds.release(Id); - } - } - - void destroyIndirectStubsManager(ResourceIdMgr::ResourceId Id) { - IndirectStubOwnerIds.release(Id); - if (auto Err = callB(Id)) - ES.reportError(std::move(Err)); - } - - Expected> - emitIndirectStubs(ResourceIdMgr::ResourceId Id, uint32_t NumStubsRequired) { - return callB(Id, NumStubsRequired); - } - - Expected> emitTrampolineBlock() { - return callB(); - } - - uint32_t getIndirectStubSize() const { return RemoteIndirectStubSize; } - uint32_t getPageSize() const { return RemotePageSize; } - uint32_t getPointerSize() const { return RemotePointerSize; } - - uint32_t getTrampolineSize() const { return RemoteTrampolineSize; } - - Expected> readMem(char *Dst, JITTargetAddress Src, - uint64_t Size) { - return callB(Src, Size); - } - - Error registerEHFrames(JITTargetAddress &RAddr, uint32_t Size) { - // FIXME: Duplicate error and report it via ReportError too? - return callB(RAddr, Size); - } - - JITTargetAddress reserveMem(ResourceIdMgr::ResourceId Id, uint64_t Size, - uint32_t Align) { - if (auto AddrOrErr = callB(Id, Size, Align)) - return *AddrOrErr; - else { - ES.reportError(AddrOrErr.takeError()); - return 0; - } - } - - bool setProtections(ResourceIdMgr::ResourceId Id, - JITTargetAddress RemoteSegAddr, unsigned ProtFlags) { - if (auto Err = callB(Id, RemoteSegAddr, ProtFlags)) { - ES.reportError(std::move(Err)); - return true; - } else - return false; - } - - bool writeMem(JITTargetAddress Addr, const char *Src, uint64_t Size) { - if (auto Err = callB(DirectBufferWriter(Src, Addr, Size))) { - ES.reportError(std::move(Err)); - return true; - } else - return false; - } - - Error writePointer(JITTargetAddress Addr, JITTargetAddress PtrVal) { - return callB(Addr, PtrVal); - } - - static Error doNothing() { return Error::success(); } - - ExecutionSession &ES; - std::function ReportError; - std::string RemoteTargetTriple; - uint32_t RemotePointerSize = 0; - uint32_t RemotePageSize = 0; - uint32_t RemoteTrampolineSize = 0; - uint32_t RemoteIndirectStubSize = 0; - ResourceIdMgr AllocatorIds, IndirectStubOwnerIds; - Optional CallbackManager; -}; - -} // end namespace remote -} // end namespace orc -} // end namespace llvm - -#undef DEBUG_TYPE - -#endif // LLVM_EXECUTIONENGINE_ORC_ORCREMOTETARGETCLIENT_H diff --git a/llvm/include/llvm/ExecutionEngine/Orc/OrcRemoteTargetRPCAPI.h b/llvm/include/llvm/ExecutionEngine/Orc/OrcRemoteTargetRPCAPI.h deleted file mode 100644 index 367bfb369191..000000000000 --- a/llvm/include/llvm/ExecutionEngine/Orc/OrcRemoteTargetRPCAPI.h +++ /dev/null @@ -1,386 +0,0 @@ -//===- OrcRemoteTargetRPCAPI.h - Orc Remote-target RPC API ------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file defines the Orc remote-target RPC API. It should not be used -// directly, but is used by the RemoteTargetClient and RemoteTargetServer -// classes. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_EXECUTIONENGINE_ORC_ORCREMOTETARGETRPCAPI_H -#define LLVM_EXECUTIONENGINE_ORC_ORCREMOTETARGETRPCAPI_H - -#include "llvm/ExecutionEngine/JITSymbol.h" -#include "llvm/ExecutionEngine/Orc/Shared/RPCUtils.h" -#include "llvm/ExecutionEngine/Orc/Shared/RawByteChannel.h" - -namespace llvm { -namespace orc { - -namespace remote { - -/// Template error for missing resources. -template -class ResourceNotFound - : public ErrorInfo> { -public: - static char ID; - - ResourceNotFound(ResourceIdT ResourceId, - std::string ResourceDescription = "") - : ResourceId(std::move(ResourceId)), - ResourceDescription(std::move(ResourceDescription)) {} - - std::error_code convertToErrorCode() const override { - return orcError(OrcErrorCode::UnknownResourceHandle); - } - - void log(raw_ostream &OS) const override { - OS << (ResourceDescription.empty() - ? "Remote resource with id " - : ResourceDescription) - << " " << ResourceId << " not found"; - } - -private: - ResourceIdT ResourceId; - std::string ResourceDescription; -}; - -template -char ResourceNotFound::ID = 0; - -class DirectBufferWriter { -public: - DirectBufferWriter() = default; - DirectBufferWriter(const char *Src, JITTargetAddress Dst, uint64_t Size) - : Src(Src), Dst(Dst), Size(Size) {} - - const char *getSrc() const { return Src; } - JITTargetAddress getDst() const { return Dst; } - uint64_t getSize() const { return Size; } - -private: - const char *Src; - JITTargetAddress Dst; - uint64_t Size; -}; - -} // end namespace remote - -namespace shared { - -template <> class SerializationTypeName { -public: - static const char *getName() { return "JITSymbolFlags"; } -}; - -template -class SerializationTraits { -public: - - static Error serialize(ChannelT &C, const JITSymbolFlags &Flags) { - return serializeSeq(C, Flags.getRawFlagsValue(), Flags.getTargetFlags()); - } - - static Error deserialize(ChannelT &C, JITSymbolFlags &Flags) { - JITSymbolFlags::UnderlyingType JITFlags; - JITSymbolFlags::TargetFlagsType TargetFlags; - if (auto Err = deserializeSeq(C, JITFlags, TargetFlags)) - return Err; - Flags = JITSymbolFlags(static_cast(JITFlags), - TargetFlags); - return Error::success(); - } -}; - -template <> class SerializationTypeName { -public: - static const char *getName() { return "DirectBufferWriter"; } -}; - -template -class SerializationTraits< - ChannelT, remote::DirectBufferWriter, remote::DirectBufferWriter, - std::enable_if_t::value>> { -public: - static Error serialize(ChannelT &C, const remote::DirectBufferWriter &DBW) { - if (auto EC = serializeSeq(C, DBW.getDst())) - return EC; - if (auto EC = serializeSeq(C, DBW.getSize())) - return EC; - return C.appendBytes(DBW.getSrc(), DBW.getSize()); - } - - static Error deserialize(ChannelT &C, remote::DirectBufferWriter &DBW) { - JITTargetAddress Dst; - if (auto EC = deserializeSeq(C, Dst)) - return EC; - uint64_t Size; - if (auto EC = deserializeSeq(C, Size)) - return EC; - char *Addr = reinterpret_cast(static_cast(Dst)); - - DBW = remote::DirectBufferWriter(nullptr, Dst, Size); - - return C.readBytes(Addr, Size); - } -}; - -} // end namespace shared - -namespace remote { - -class ResourceIdMgr { -public: - using ResourceId = uint64_t; - static const ResourceId InvalidId = ~0U; - - ResourceIdMgr() = default; - explicit ResourceIdMgr(ResourceId FirstValidId) - : NextId(std::move(FirstValidId)) {} - - ResourceId getNext() { - if (!FreeIds.empty()) { - ResourceId I = FreeIds.back(); - FreeIds.pop_back(); - return I; - } - assert(NextId + 1 != ~0ULL && "All ids allocated"); - return NextId++; - } - - void release(ResourceId I) { FreeIds.push_back(I); } - -private: - ResourceId NextId = 1; - std::vector FreeIds; -}; - -/// Registers EH frames on the remote. -namespace eh { - - /// Registers EH frames on the remote. -class RegisterEHFrames - : public shared::RPCFunction { -public: - static const char *getName() { return "RegisterEHFrames"; } -}; - - /// Deregisters EH frames on the remote. -class DeregisterEHFrames - : public shared::RPCFunction { -public: - static const char *getName() { return "DeregisterEHFrames"; } -}; - -} // end namespace eh - -/// RPC functions for executing remote code. -namespace exec { - - /// Call an 'int32_t()'-type function on the remote, returns the called - /// function's return value. -class CallIntVoid - : public shared::RPCFunction { -public: - static const char *getName() { return "CallIntVoid"; } -}; - - /// Call an 'int32_t(int32_t)'-type function on the remote, returns the called - /// function's return value. -class CallIntInt - : public shared::RPCFunction { -public: - static const char *getName() { return "CallIntInt"; } -}; - - /// Call an 'int32_t(int32_t, char**)'-type function on the remote, returns the - /// called function's return value. -class CallMain - : public shared::RPCFunction Args)> { -public: - static const char *getName() { return "CallMain"; } -}; - - /// Calls a 'void()'-type function on the remote, returns when the called - /// function completes. -class CallVoidVoid - : public shared::RPCFunction { -public: - static const char *getName() { return "CallVoidVoid"; } -}; - -} // end namespace exec - -/// RPC functions for remote memory management / inspection / modification. -namespace mem { - - /// Creates a memory allocator on the remote. -class CreateRemoteAllocator - : public shared::RPCFunction { -public: - static const char *getName() { return "CreateRemoteAllocator"; } -}; - - /// Destroys a remote allocator, freeing any memory allocated by it. -class DestroyRemoteAllocator - : public shared::RPCFunction { -public: - static const char *getName() { return "DestroyRemoteAllocator"; } -}; - - /// Read a remote memory block. -class ReadMem - : public shared::RPCFunction< - ReadMem, std::vector(JITTargetAddress Src, uint64_t Size)> { -public: - static const char *getName() { return "ReadMem"; } -}; - - /// Reserve a block of memory on the remote via the given allocator. -class ReserveMem - : public shared::RPCFunction< - ReserveMem, JITTargetAddress(ResourceIdMgr::ResourceId AllocID, - uint64_t Size, uint32_t Align)> { -public: - static const char *getName() { return "ReserveMem"; } -}; - - /// Set the memory protection on a memory block. -class SetProtections - : public shared::RPCFunction< - SetProtections, void(ResourceIdMgr::ResourceId AllocID, - JITTargetAddress Dst, uint32_t ProtFlags)> { -public: - static const char *getName() { return "SetProtections"; } -}; - - /// Write to a remote memory block. -class WriteMem - : public shared::RPCFunction { -public: - static const char *getName() { return "WriteMem"; } -}; - - /// Write to a remote pointer. -class WritePtr - : public shared::RPCFunction { -public: - static const char *getName() { return "WritePtr"; } -}; - -} // end namespace mem - -/// RPC functions for remote stub and trampoline management. -namespace stubs { - - /// Creates an indirect stub owner on the remote. -class CreateIndirectStubsOwner - : public shared::RPCFunction { -public: - static const char *getName() { return "CreateIndirectStubsOwner"; } -}; - - /// RPC function for destroying an indirect stubs owner. -class DestroyIndirectStubsOwner - : public shared::RPCFunction { -public: - static const char *getName() { return "DestroyIndirectStubsOwner"; } -}; - - /// EmitIndirectStubs result is (StubsBase, PtrsBase, NumStubsEmitted). -class EmitIndirectStubs - : public shared::RPCFunction< - EmitIndirectStubs, - std::tuple( - ResourceIdMgr::ResourceId StubsOwnerID, - uint32_t NumStubsRequired)> { -public: - static const char *getName() { return "EmitIndirectStubs"; } -}; - - /// RPC function to emit the resolver block and return its address. -class EmitResolverBlock - : public shared::RPCFunction { -public: - static const char *getName() { return "EmitResolverBlock"; } -}; - - /// EmitTrampolineBlock result is (BlockAddr, NumTrampolines). -class EmitTrampolineBlock - : public shared::RPCFunction()> { -public: - static const char *getName() { return "EmitTrampolineBlock"; } -}; - -} // end namespace stubs - -/// Miscelaneous RPC functions for dealing with remotes. -namespace utils { - - /// GetRemoteInfo result is (Triple, PointerSize, PageSize, TrampolineSize, - /// IndirectStubsSize). -class GetRemoteInfo - : public shared::RPCFunction< - GetRemoteInfo, - std::tuple()> { -public: - static const char *getName() { return "GetRemoteInfo"; } -}; - - /// Get the address of a remote symbol. -class GetSymbolAddress - : public shared::RPCFunction { -public: - static const char *getName() { return "GetSymbolAddress"; } -}; - - /// Request that the host execute a compile callback. -class RequestCompile - : public shared::RPCFunction< - RequestCompile, JITTargetAddress(JITTargetAddress TrampolineAddr)> { -public: - static const char *getName() { return "RequestCompile"; } -}; - - /// Notify the remote and terminate the session. -class TerminateSession : public shared::RPCFunction { -public: - static const char *getName() { return "TerminateSession"; } -}; - -} // namespace utils - -class OrcRemoteTargetRPCAPI - : public shared::SingleThreadedRPCEndpoint { -public: - // FIXME: Remove constructors once MSVC supports synthesizing move-ops. - OrcRemoteTargetRPCAPI(shared::RawByteChannel &C) - : shared::SingleThreadedRPCEndpoint(C, true) {} -}; - -} // end namespace remote - -} // end namespace orc -} // end namespace llvm - -#endif // LLVM_EXECUTIONENGINE_ORC_ORCREMOTETARGETRPCAPI_H diff --git a/llvm/include/llvm/ExecutionEngine/Orc/OrcRemoteTargetServer.h b/llvm/include/llvm/ExecutionEngine/Orc/OrcRemoteTargetServer.h deleted file mode 100644 index ce9bf064303d..000000000000 --- a/llvm/include/llvm/ExecutionEngine/Orc/OrcRemoteTargetServer.h +++ /dev/null @@ -1,464 +0,0 @@ -//===- OrcRemoteTargetServer.h - Orc Remote-target Server -------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file defines the OrcRemoteTargetServer class. It can be used to build a -// JIT server that can execute code sent from an OrcRemoteTargetClient. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_EXECUTIONENGINE_ORC_ORCREMOTETARGETSERVER_H -#define LLVM_EXECUTIONENGINE_ORC_ORCREMOTETARGETSERVER_H - -#include "llvm/ExecutionEngine/JITSymbol.h" -#include "llvm/ExecutionEngine/Orc/IndirectionUtils.h" -#include "llvm/ExecutionEngine/Orc/OrcRemoteTargetRPCAPI.h" -#include "llvm/ExecutionEngine/Orc/Shared/OrcError.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/Error.h" -#include "llvm/Support/Format.h" -#include "llvm/Support/Host.h" -#include "llvm/Support/Memory.h" -#include "llvm/Support/Process.h" -#include "llvm/Support/raw_ostream.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DEBUG_TYPE "orc-remote" - -namespace llvm { -namespace orc { -namespace remote { - -template -class OrcRemoteTargetServer - : public shared::SingleThreadedRPCEndpoint { -public: - using SymbolLookupFtor = - std::function; - - using EHFrameRegistrationFtor = - std::function; - - OrcRemoteTargetServer(ChannelT &Channel, SymbolLookupFtor SymbolLookup, - EHFrameRegistrationFtor EHFramesRegister, - EHFrameRegistrationFtor EHFramesDeregister) - : shared::SingleThreadedRPCEndpoint(Channel, - true), - SymbolLookup(std::move(SymbolLookup)), - EHFramesRegister(std::move(EHFramesRegister)), - EHFramesDeregister(std::move(EHFramesDeregister)) { - using ThisT = std::remove_reference_t; - addHandler(*this, &ThisT::handleCallIntVoid); - addHandler(*this, &ThisT::handleCallIntInt); - addHandler(*this, &ThisT::handleCallMain); - addHandler(*this, &ThisT::handleCallVoidVoid); - addHandler(*this, - &ThisT::handleCreateRemoteAllocator); - addHandler( - *this, &ThisT::handleDestroyRemoteAllocator); - addHandler(*this, &ThisT::handleReadMem); - addHandler(*this, &ThisT::handleReserveMem); - addHandler(*this, &ThisT::handleSetProtections); - addHandler(*this, &ThisT::handleWriteMem); - addHandler(*this, &ThisT::handleWritePtr); - addHandler(*this, &ThisT::handleRegisterEHFrames); - addHandler(*this, &ThisT::handleDeregisterEHFrames); - addHandler( - *this, &ThisT::handleCreateIndirectStubsOwner); - addHandler( - *this, &ThisT::handleDestroyIndirectStubsOwner); - addHandler(*this, - &ThisT::handleEmitIndirectStubs); - addHandler(*this, - &ThisT::handleEmitResolverBlock); - addHandler(*this, - &ThisT::handleEmitTrampolineBlock); - addHandler(*this, &ThisT::handleGetSymbolAddress); - addHandler(*this, &ThisT::handleGetRemoteInfo); - addHandler(*this, &ThisT::handleTerminateSession); - } - - // FIXME: Remove move/copy ops once MSVC supports synthesizing move ops. - OrcRemoteTargetServer(const OrcRemoteTargetServer &) = delete; - OrcRemoteTargetServer &operator=(const OrcRemoteTargetServer &) = delete; - - OrcRemoteTargetServer(OrcRemoteTargetServer &&Other) = default; - OrcRemoteTargetServer &operator=(OrcRemoteTargetServer &&) = delete; - - Expected requestCompile(JITTargetAddress TrampolineAddr) { - return callB(TrampolineAddr); - } - - bool receivedTerminate() const { return TerminateFlag; } - -private: - struct Allocator { - Allocator() = default; - Allocator(Allocator &&Other) : Allocs(std::move(Other.Allocs)) {} - - Allocator &operator=(Allocator &&Other) { - Allocs = std::move(Other.Allocs); - return *this; - } - - ~Allocator() { - for (auto &Alloc : Allocs) - sys::Memory::releaseMappedMemory(Alloc.second); - } - - Error allocate(void *&Addr, size_t Size, uint32_t Align) { - std::error_code EC; - sys::MemoryBlock MB = sys::Memory::allocateMappedMemory( - Size, nullptr, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC); - if (EC) - return errorCodeToError(EC); - - Addr = MB.base(); - assert(Allocs.find(MB.base()) == Allocs.end() && "Duplicate alloc"); - Allocs[MB.base()] = std::move(MB); - return Error::success(); - } - - Error setProtections(void *block, unsigned Flags) { - auto I = Allocs.find(block); - if (I == Allocs.end()) - return errorCodeToError(orcError(OrcErrorCode::RemoteMProtectAddrUnrecognized)); - return errorCodeToError( - sys::Memory::protectMappedMemory(I->second, Flags)); - } - - private: - std::map Allocs; - }; - - static Error doNothing() { return Error::success(); } - - static JITTargetAddress reenter(void *JITTargetAddr, void *TrampolineAddr) { - auto T = static_cast(JITTargetAddr); - auto AddrOrErr = T->requestCompile(static_cast( - reinterpret_cast(TrampolineAddr))); - // FIXME: Allow customizable failure substitution functions. - assert(AddrOrErr && "Compile request failed"); - return *AddrOrErr; - } - - Expected handleCallIntVoid(JITTargetAddress Addr) { - using IntVoidFnTy = int (*)(); - - IntVoidFnTy Fn = - reinterpret_cast(static_cast(Addr)); - - LLVM_DEBUG(dbgs() << " Calling " << format("0x%016x", Addr) << "\n"); - int Result = Fn(); - LLVM_DEBUG(dbgs() << " Result = " << Result << "\n"); - - return Result; - } - - Expected handleCallIntInt(JITTargetAddress Addr, int Arg) { - using IntIntFnTy = int (*)(int); - - IntIntFnTy Fn = reinterpret_cast(static_cast(Addr)); - - LLVM_DEBUG(dbgs() << " Calling " << format("0x%016x", Addr) - << " with argument " << Arg << "\n"); - int Result = Fn(Arg); - LLVM_DEBUG(dbgs() << " Result = " << Result << "\n"); - - return Result; - } - - Expected handleCallMain(JITTargetAddress Addr, - std::vector Args) { - using MainFnTy = int (*)(int, const char *[]); - - MainFnTy Fn = reinterpret_cast(static_cast(Addr)); - int ArgC = Args.size() + 1; - int Idx = 1; - std::unique_ptr ArgV(new const char *[ArgC + 1]); - ArgV[0] = ""; - for (auto &Arg : Args) - ArgV[Idx++] = Arg.c_str(); - ArgV[ArgC] = 0; - LLVM_DEBUG(for (int Idx = 0; Idx < ArgC; ++Idx) { - llvm::dbgs() << "Arg " << Idx << ": " << ArgV[Idx] << "\n"; - }); - - LLVM_DEBUG(dbgs() << " Calling " << format("0x%016x", Addr) << "\n"); - int Result = Fn(ArgC, ArgV.get()); - LLVM_DEBUG(dbgs() << " Result = " << Result << "\n"); - - return Result; - } - - Error handleCallVoidVoid(JITTargetAddress Addr) { - using VoidVoidFnTy = void (*)(); - - VoidVoidFnTy Fn = - reinterpret_cast(static_cast(Addr)); - - LLVM_DEBUG(dbgs() << " Calling " << format("0x%016x", Addr) << "\n"); - Fn(); - LLVM_DEBUG(dbgs() << " Complete.\n"); - - return Error::success(); - } - - Error handleCreateRemoteAllocator(ResourceIdMgr::ResourceId Id) { - auto I = Allocators.find(Id); - if (I != Allocators.end()) - return errorCodeToError( - orcError(OrcErrorCode::RemoteAllocatorIdAlreadyInUse)); - LLVM_DEBUG(dbgs() << " Created allocator " << Id << "\n"); - Allocators[Id] = Allocator(); - return Error::success(); - } - - Error handleCreateIndirectStubsOwner(ResourceIdMgr::ResourceId Id) { - auto I = IndirectStubsOwners.find(Id); - if (I != IndirectStubsOwners.end()) - return errorCodeToError( - orcError(OrcErrorCode::RemoteIndirectStubsOwnerIdAlreadyInUse)); - LLVM_DEBUG(dbgs() << " Create indirect stubs owner " << Id << "\n"); - IndirectStubsOwners[Id] = ISBlockOwnerList(); - return Error::success(); - } - - Error handleDeregisterEHFrames(JITTargetAddress TAddr, uint32_t Size) { - uint8_t *Addr = reinterpret_cast(static_cast(TAddr)); - LLVM_DEBUG(dbgs() << " Registering EH frames at " - << format("0x%016x", TAddr) << ", Size = " << Size - << " bytes\n"); - EHFramesDeregister(Addr, Size); - return Error::success(); - } - - Error handleDestroyRemoteAllocator(ResourceIdMgr::ResourceId Id) { - auto I = Allocators.find(Id); - if (I == Allocators.end()) - return errorCodeToError( - orcError(OrcErrorCode::RemoteAllocatorDoesNotExist)); - Allocators.erase(I); - LLVM_DEBUG(dbgs() << " Destroyed allocator " << Id << "\n"); - return Error::success(); - } - - Error handleDestroyIndirectStubsOwner(ResourceIdMgr::ResourceId Id) { - auto I = IndirectStubsOwners.find(Id); - if (I == IndirectStubsOwners.end()) - return errorCodeToError( - orcError(OrcErrorCode::RemoteIndirectStubsOwnerDoesNotExist)); - IndirectStubsOwners.erase(I); - return Error::success(); - } - - Expected> - handleEmitIndirectStubs(ResourceIdMgr::ResourceId Id, - uint32_t NumStubsRequired) { - LLVM_DEBUG(dbgs() << " ISMgr " << Id << " request " << NumStubsRequired - << " stubs.\n"); - - auto StubOwnerItr = IndirectStubsOwners.find(Id); - if (StubOwnerItr == IndirectStubsOwners.end()) - return errorCodeToError( - orcError(OrcErrorCode::RemoteIndirectStubsOwnerDoesNotExist)); - - auto IS = LocalIndirectStubsInfo::create( - NumStubsRequired, sys::Process::getPageSizeEstimate()); - if (!IS) - return IS.takeError(); - - JITTargetAddress StubsBase = pointerToJITTargetAddress(IS->getStub(0)); - JITTargetAddress PtrsBase = pointerToJITTargetAddress(IS->getPtr(0)); - uint32_t NumStubsEmitted = IS->getNumStubs(); - - auto &BlockList = StubOwnerItr->second; - BlockList.push_back(std::move(*IS)); - - return std::make_tuple(StubsBase, PtrsBase, NumStubsEmitted); - } - - Error handleEmitResolverBlock() { - std::error_code EC; - ResolverBlock = sys::OwningMemoryBlock(sys::Memory::allocateMappedMemory( - TargetT::ResolverCodeSize, nullptr, - sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC)); - if (EC) - return errorCodeToError(EC); - - TargetT::writeResolverCode(static_cast(ResolverBlock.base()), - pointerToJITTargetAddress(ResolverBlock.base()), - pointerToJITTargetAddress(&reenter), - pointerToJITTargetAddress(this)); - - return errorCodeToError(sys::Memory::protectMappedMemory( - ResolverBlock.getMemoryBlock(), - sys::Memory::MF_READ | sys::Memory::MF_EXEC)); - } - - Expected> handleEmitTrampolineBlock() { - std::error_code EC; - auto TrampolineBlock = - sys::OwningMemoryBlock(sys::Memory::allocateMappedMemory( - sys::Process::getPageSizeEstimate(), nullptr, - sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC)); - if (EC) - return errorCodeToError(EC); - - uint32_t NumTrampolines = - (sys::Process::getPageSizeEstimate() - TargetT::PointerSize) / - TargetT::TrampolineSize; - - char *TrampolineMem = static_cast(TrampolineBlock.base()); - TargetT::writeTrampolines( - TrampolineMem, pointerToJITTargetAddress(TrampolineMem), - pointerToJITTargetAddress(ResolverBlock.base()), NumTrampolines); - - EC = sys::Memory::protectMappedMemory(TrampolineBlock.getMemoryBlock(), - sys::Memory::MF_READ | - sys::Memory::MF_EXEC); - - TrampolineBlocks.push_back(std::move(TrampolineBlock)); - - return std::make_tuple(pointerToJITTargetAddress(TrampolineMem), - NumTrampolines); - } - - Expected handleGetSymbolAddress(const std::string &Name) { - JITTargetAddress Addr = SymbolLookup(Name); - LLVM_DEBUG(dbgs() << " Symbol '" << Name - << "' = " << format("0x%016x", Addr) << "\n"); - return Addr; - } - - Expected> - handleGetRemoteInfo() { - std::string ProcessTriple = sys::getProcessTriple(); - uint32_t PointerSize = TargetT::PointerSize; - uint32_t PageSize = sys::Process::getPageSizeEstimate(); - uint32_t TrampolineSize = TargetT::TrampolineSize; - uint32_t IndirectStubSize = TargetT::StubSize; - LLVM_DEBUG(dbgs() << " Remote info:\n" - << " triple = '" << ProcessTriple << "'\n" - << " pointer size = " << PointerSize << "\n" - << " page size = " << PageSize << "\n" - << " trampoline size = " << TrampolineSize << "\n" - << " indirect stub size = " << IndirectStubSize - << "\n"); - return std::make_tuple(ProcessTriple, PointerSize, PageSize, TrampolineSize, - IndirectStubSize); - } - - Expected> handleReadMem(JITTargetAddress RSrc, - uint64_t Size) { - uint8_t *Src = reinterpret_cast(static_cast(RSrc)); - - LLVM_DEBUG(dbgs() << " Reading " << Size << " bytes from " - << format("0x%016x", RSrc) << "\n"); - - std::vector Buffer; - Buffer.resize(Size); - for (uint8_t *P = Src; Size != 0; --Size) - Buffer.push_back(*P++); - - return Buffer; - } - - Error handleRegisterEHFrames(JITTargetAddress TAddr, uint32_t Size) { - uint8_t *Addr = reinterpret_cast(static_cast(TAddr)); - LLVM_DEBUG(dbgs() << " Registering EH frames at " - << format("0x%016x", TAddr) << ", Size = " << Size - << " bytes\n"); - EHFramesRegister(Addr, Size); - return Error::success(); - } - - Expected handleReserveMem(ResourceIdMgr::ResourceId Id, - uint64_t Size, uint32_t Align) { - auto I = Allocators.find(Id); - if (I == Allocators.end()) - return errorCodeToError( - orcError(OrcErrorCode::RemoteAllocatorDoesNotExist)); - auto &Allocator = I->second; - void *LocalAllocAddr = nullptr; - if (auto Err = Allocator.allocate(LocalAllocAddr, Size, Align)) - return std::move(Err); - - LLVM_DEBUG(dbgs() << " Allocator " << Id << " reserved " << LocalAllocAddr - << " (" << Size << " bytes, alignment " << Align - << ")\n"); - - JITTargetAddress AllocAddr = static_cast( - reinterpret_cast(LocalAllocAddr)); - - return AllocAddr; - } - - Error handleSetProtections(ResourceIdMgr::ResourceId Id, - JITTargetAddress Addr, uint32_t Flags) { - auto I = Allocators.find(Id); - if (I == Allocators.end()) - return errorCodeToError( - orcError(OrcErrorCode::RemoteAllocatorDoesNotExist)); - auto &Allocator = I->second; - void *LocalAddr = reinterpret_cast(static_cast(Addr)); - LLVM_DEBUG(dbgs() << " Allocator " << Id << " set permissions on " - << LocalAddr << " to " - << (Flags & sys::Memory::MF_READ ? 'R' : '-') - << (Flags & sys::Memory::MF_WRITE ? 'W' : '-') - << (Flags & sys::Memory::MF_EXEC ? 'X' : '-') << "\n"); - return Allocator.setProtections(LocalAddr, Flags); - } - - Error handleTerminateSession() { - TerminateFlag = true; - return Error::success(); - } - - Error handleWriteMem(DirectBufferWriter DBW) { - LLVM_DEBUG(dbgs() << " Writing " << DBW.getSize() << " bytes to " - << format("0x%016x", DBW.getDst()) << "\n"); - return Error::success(); - } - - Error handleWritePtr(JITTargetAddress Addr, JITTargetAddress PtrVal) { - LLVM_DEBUG(dbgs() << " Writing pointer *" << format("0x%016x", Addr) - << " = " << format("0x%016x", PtrVal) << "\n"); - uintptr_t *Ptr = - reinterpret_cast(static_cast(Addr)); - *Ptr = static_cast(PtrVal); - return Error::success(); - } - - SymbolLookupFtor SymbolLookup; - EHFrameRegistrationFtor EHFramesRegister, EHFramesDeregister; - std::map Allocators; - using ISBlockOwnerList = std::vector>; - std::map IndirectStubsOwners; - sys::OwningMemoryBlock ResolverBlock; - std::vector TrampolineBlocks; - bool TerminateFlag = false; -}; - -} // end namespace remote -} // end namespace orc -} // end namespace llvm - -#undef DEBUG_TYPE - -#endif // LLVM_EXECUTIONENGINE_ORC_ORCREMOTETARGETSERVER_H diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h index 78a6623d7594..3c0b2b9edd52 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h @@ -34,25 +34,26 @@ private: }; /// Represents an address in the executor process. -class ExecutorAddress { +class ExecutorAddr { public: - ExecutorAddress() = default; - explicit ExecutorAddress(uint64_t Addr) : Addr(Addr) {} + ExecutorAddr() = default; - /// Create an ExecutorAddress from the given pointer. + /// Create an ExecutorAddr from the given value. + explicit ExecutorAddr(uint64_t Addr) : Addr(Addr) {} + + /// Create an ExecutorAddr from the given pointer. /// Warning: This should only be used when JITing in-process. - template static ExecutorAddress fromPtr(T *Value) { - return ExecutorAddress( + template static ExecutorAddr fromPtr(T *Value) { + return ExecutorAddr( static_cast(reinterpret_cast(Value))); } - /// Cast this ExecutorAddress to a pointer of the given type. - /// Warning: This should only be esude when JITing in-process. + /// Cast this ExecutorAddr to a pointer of the given type. + /// Warning: This should only be used when JITing in-process. template T toPtr() const { static_assert(std::is_pointer::value, "T must be a pointer type"); uintptr_t IntPtr = static_cast(Addr); - assert(IntPtr == Addr && - "JITTargetAddress value out of range for uintptr_t"); + assert(IntPtr == Addr && "ExecutorAddr value out of range for uintptr_t"); return reinterpret_cast(IntPtr); } @@ -62,53 +63,47 @@ public: explicit operator bool() const { return Addr != 0; } - friend bool operator==(const ExecutorAddress &LHS, - const ExecutorAddress &RHS) { + friend bool operator==(const ExecutorAddr &LHS, const ExecutorAddr &RHS) { return LHS.Addr == RHS.Addr; } - friend bool operator!=(const ExecutorAddress &LHS, - const ExecutorAddress &RHS) { + friend bool operator!=(const ExecutorAddr &LHS, const ExecutorAddr &RHS) { return LHS.Addr != RHS.Addr; } - friend bool operator<(const ExecutorAddress &LHS, - const ExecutorAddress &RHS) { + friend bool operator<(const ExecutorAddr &LHS, const ExecutorAddr &RHS) { return LHS.Addr < RHS.Addr; } - friend bool operator<=(const ExecutorAddress &LHS, - const ExecutorAddress &RHS) { + friend bool operator<=(const ExecutorAddr &LHS, const ExecutorAddr &RHS) { return LHS.Addr <= RHS.Addr; } - friend bool operator>(const ExecutorAddress &LHS, - const ExecutorAddress &RHS) { + friend bool operator>(const ExecutorAddr &LHS, const ExecutorAddr &RHS) { return LHS.Addr > RHS.Addr; } - friend bool operator>=(const ExecutorAddress &LHS, - const ExecutorAddress &RHS) { + friend bool operator>=(const ExecutorAddr &LHS, const ExecutorAddr &RHS) { return LHS.Addr >= RHS.Addr; } - ExecutorAddress &operator++() { + ExecutorAddr &operator++() { ++Addr; return *this; } - ExecutorAddress &operator--() { + ExecutorAddr &operator--() { --Addr; return *this; } - ExecutorAddress operator++(int) { return ExecutorAddress(Addr++); } - ExecutorAddress operator--(int) { return ExecutorAddress(Addr++); } + ExecutorAddr operator++(int) { return ExecutorAddr(Addr++); } + ExecutorAddr operator--(int) { return ExecutorAddr(Addr--); } - ExecutorAddress &operator+=(const ExecutorAddrDiff Delta) { + ExecutorAddr &operator+=(const ExecutorAddrDiff Delta) { Addr += Delta.getValue(); return *this; } - ExecutorAddress &operator-=(const ExecutorAddrDiff Delta) { + ExecutorAddr &operator-=(const ExecutorAddrDiff Delta) { Addr -= Delta.getValue(); return *this; } @@ -118,83 +113,98 @@ private: }; /// Subtracting two addresses yields an offset. -inline ExecutorAddrDiff operator-(const ExecutorAddress &LHS, - const ExecutorAddress &RHS) { +inline ExecutorAddrDiff operator-(const ExecutorAddr &LHS, + const ExecutorAddr &RHS) { return ExecutorAddrDiff(LHS.getValue() - RHS.getValue()); } /// Adding an offset and an address yields an address. -inline ExecutorAddress operator+(const ExecutorAddress &LHS, - const ExecutorAddrDiff &RHS) { - return ExecutorAddress(LHS.getValue() + RHS.getValue()); +inline ExecutorAddr operator+(const ExecutorAddr &LHS, + const ExecutorAddrDiff &RHS) { + return ExecutorAddr(LHS.getValue() + RHS.getValue()); } /// Adding an address and an offset yields an address. -inline ExecutorAddress operator+(const ExecutorAddrDiff &LHS, - const ExecutorAddress &RHS) { - return ExecutorAddress(LHS.getValue() + RHS.getValue()); +inline ExecutorAddr operator+(const ExecutorAddrDiff &LHS, + const ExecutorAddr &RHS) { + return ExecutorAddr(LHS.getValue() + RHS.getValue()); } /// Represents an address range in the exceutor process. -struct ExecutorAddressRange { - ExecutorAddressRange() = default; - ExecutorAddressRange(ExecutorAddress StartAddress, ExecutorAddress EndAddress) - : StartAddress(StartAddress), EndAddress(EndAddress) {} +struct ExecutorAddrRange { + ExecutorAddrRange() = default; + ExecutorAddrRange(ExecutorAddr Start, ExecutorAddr End) + : Start(Start), End(End) {} + ExecutorAddrRange(ExecutorAddr Start, ExecutorAddrDiff Size) + : Start(Start), End(Start + Size) {} - bool empty() const { return StartAddress == EndAddress; } - ExecutorAddrDiff size() const { return EndAddress - StartAddress; } + bool empty() const { return Start == End; } + ExecutorAddrDiff size() const { return End - Start; } - ExecutorAddress StartAddress; - ExecutorAddress EndAddress; + friend bool operator==(const ExecutorAddrRange &LHS, + const ExecutorAddrRange &RHS) { + return LHS.Start == RHS.Start && LHS.End == RHS.End; + } + friend bool operator!=(const ExecutorAddrRange &LHS, + const ExecutorAddrRange &RHS) { + return !(LHS == RHS); + } + bool contains(ExecutorAddr Addr) const { return Start <= Addr && Addr < End; } + bool overlaps(const ExecutorAddrRange &Other) { + return !(Other.End <= Start || End <= Other.Start); + } + + ExecutorAddr Start; + ExecutorAddr End; }; namespace shared { -/// SPS serializatior for ExecutorAddress. -template <> class SPSSerializationTraits { +class SPSExecutorAddr {}; + +/// SPS serializatior for ExecutorAddr. +template <> class SPSSerializationTraits { public: - static size_t size(const ExecutorAddress &EA) { + static size_t size(const ExecutorAddr &EA) { return SPSArgList::size(EA.getValue()); } - static bool serialize(SPSOutputBuffer &BOB, const ExecutorAddress &EA) { + static bool serialize(SPSOutputBuffer &BOB, const ExecutorAddr &EA) { return SPSArgList::serialize(BOB, EA.getValue()); } - static bool deserialize(SPSInputBuffer &BIB, ExecutorAddress &EA) { + static bool deserialize(SPSInputBuffer &BIB, ExecutorAddr &EA) { uint64_t Tmp; if (!SPSArgList::deserialize(BIB, Tmp)) return false; - EA = ExecutorAddress(Tmp); + EA = ExecutorAddr(Tmp); return true; } }; -using SPSExecutorAddressRange = - SPSTuple; +using SPSExecutorAddrRange = SPSTuple; /// Serialization traits for address ranges. template <> -class SPSSerializationTraits { +class SPSSerializationTraits { public: - static size_t size(const ExecutorAddressRange &Value) { - return SPSArgList::size( - Value.StartAddress, Value.EndAddress); + static size_t size(const ExecutorAddrRange &Value) { + return SPSArgList::size(Value.Start, + Value.End); } - static bool serialize(SPSOutputBuffer &BOB, - const ExecutorAddressRange &Value) { - return SPSArgList::serialize( - BOB, Value.StartAddress, Value.EndAddress); + static bool serialize(SPSOutputBuffer &BOB, const ExecutorAddrRange &Value) { + return SPSArgList::serialize( + BOB, Value.Start, Value.End); } - static bool deserialize(SPSInputBuffer &BIB, ExecutorAddressRange &Value) { - return SPSArgList::deserialize( - BIB, Value.StartAddress, Value.EndAddress); + static bool deserialize(SPSInputBuffer &BIB, ExecutorAddrRange &Value) { + return SPSArgList::deserialize( + BIB, Value.Start, Value.End); } }; -using SPSExecutorAddressRangeSequence = SPSSequence; +using SPSExecutorAddrRangeSequence = SPSSequence; } // End namespace shared. } // End namespace orc. diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/FDRawByteChannel.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/FDRawByteChannel.h deleted file mode 100644 index 3f96fe3da49d..000000000000 --- a/llvm/include/llvm/ExecutionEngine/Orc/Shared/FDRawByteChannel.h +++ /dev/null @@ -1,79 +0,0 @@ -//===- FDRawByteChannel.h - File descriptor based byte-channel -*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// File descriptor based RawByteChannel. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_EXECUTIONENGINE_ORC_SHARED_FDRAWBYTECHANNEL_H -#define LLVM_EXECUTIONENGINE_ORC_SHARED_FDRAWBYTECHANNEL_H - -#include "llvm/ExecutionEngine/Orc/Shared/RawByteChannel.h" - -#if !defined(_MSC_VER) && !defined(__MINGW32__) -#include -#else -#include -#endif - -namespace llvm { -namespace orc { -namespace shared { - -/// Serialization channel that reads from and writes from file descriptors. -class FDRawByteChannel final : public RawByteChannel { -public: - FDRawByteChannel(int InFD, int OutFD) : InFD(InFD), OutFD(OutFD) {} - - llvm::Error readBytes(char *Dst, unsigned Size) override { - assert(Dst && "Attempt to read into null."); - ssize_t Completed = 0; - while (Completed < static_cast(Size)) { - ssize_t Read = ::read(InFD, Dst + Completed, Size - Completed); - if (Read <= 0) { - auto ErrNo = errno; - if (ErrNo == EAGAIN || ErrNo == EINTR) - continue; - else - return llvm::errorCodeToError( - std::error_code(errno, std::generic_category())); - } - Completed += Read; - } - return llvm::Error::success(); - } - - llvm::Error appendBytes(const char *Src, unsigned Size) override { - assert(Src && "Attempt to append from null."); - ssize_t Completed = 0; - while (Completed < static_cast(Size)) { - ssize_t Written = ::write(OutFD, Src + Completed, Size - Completed); - if (Written < 0) { - auto ErrNo = errno; - if (ErrNo == EAGAIN || ErrNo == EINTR) - continue; - else - return llvm::errorCodeToError( - std::error_code(errno, std::generic_category())); - } - Completed += Written; - } - return llvm::Error::success(); - } - - llvm::Error send() override { return llvm::Error::success(); } - -private: - int InFD, OutFD; -}; - -} // namespace shared -} // namespace orc -} // namespace llvm - -#endif // LLVM_EXECUTIONENGINE_ORC_SHARED_FDRAWBYTECHANNEL_H diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h new file mode 100644 index 000000000000..3ef43f33d84c --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h @@ -0,0 +1,68 @@ +//===---- OrcRTBridge.h -- Utils for interacting with orc-rt ----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Declares types and symbol names provided by the ORC runtime. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_ORC_SHARED_ORCRTBRIDGE_H +#define LLVM_EXECUTIONENGINE_ORC_SHARED_ORCRTBRIDGE_H + +#include "llvm/ADT/StringMap.h" +#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h" +#include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h" +#include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h" + +namespace llvm { +namespace orc { +namespace rt { + +extern const char *SimpleExecutorDylibManagerInstanceName; +extern const char *SimpleExecutorDylibManagerOpenWrapperName; +extern const char *SimpleExecutorDylibManagerLookupWrapperName; + +extern const char *SimpleExecutorMemoryManagerInstanceName; +extern const char *SimpleExecutorMemoryManagerReserveWrapperName; +extern const char *SimpleExecutorMemoryManagerFinalizeWrapperName; +extern const char *SimpleExecutorMemoryManagerDeallocateWrapperName; + +extern const char *MemoryWriteUInt8sWrapperName; +extern const char *MemoryWriteUInt16sWrapperName; +extern const char *MemoryWriteUInt32sWrapperName; +extern const char *MemoryWriteUInt64sWrapperName; +extern const char *MemoryWriteBuffersWrapperName; + +extern const char *RegisterEHFrameSectionCustomDirectWrapperName; +extern const char *DeregisterEHFrameSectionCustomDirectWrapperName; + +extern const char *RunAsMainWrapperName; + +using SPSSimpleExecutorDylibManagerOpenSignature = + shared::SPSExpected(shared::SPSExecutorAddr, shared::SPSString, + uint64_t); + +using SPSSimpleExecutorDylibManagerLookupSignature = + shared::SPSExpected>( + shared::SPSExecutorAddr, uint64_t, shared::SPSRemoteSymbolLookupSet); + +using SPSSimpleExecutorMemoryManagerReserveSignature = + shared::SPSExpected(shared::SPSExecutorAddr, + uint64_t); +using SPSSimpleExecutorMemoryManagerFinalizeSignature = + shared::SPSError(shared::SPSExecutorAddr, shared::SPSFinalizeRequest); +using SPSSimpleExecutorMemoryManagerDeallocateSignature = shared::SPSError( + shared::SPSExecutorAddr, shared::SPSSequence); + +using SPSRunAsMainSignature = int64_t(shared::SPSExecutorAddr, + shared::SPSSequence); + +} // end namespace rt +} // end namespace orc +} // end namespace llvm + +#endif // LLVM_EXECUTIONENGINE_ORC_SHARED_ORCRTBRIDGE_H diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/RPCUtils.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/RPCUtils.h deleted file mode 100644 index 1ff47ce42758..000000000000 --- a/llvm/include/llvm/ExecutionEngine/Orc/Shared/RPCUtils.h +++ /dev/null @@ -1,1659 +0,0 @@ -//===- RPCUtils.h - Utilities for building RPC APIs -------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Utilities to support construction of simple RPC APIs. -// -// The RPC utilities aim for ease of use (minimal conceptual overhead) for C++ -// programmers, high performance, low memory overhead, and efficient use of the -// communications channel. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_EXECUTIONENGINE_ORC_SHARED_RPCUTILS_H -#define LLVM_EXECUTIONENGINE_ORC_SHARED_RPCUTILS_H - -#include -#include -#include - -#include "llvm/ADT/STLExtras.h" -#include "llvm/ExecutionEngine/Orc/Shared/OrcError.h" -#include "llvm/ExecutionEngine/Orc/Shared/Serialization.h" -#include "llvm/Support/MSVCErrorWorkarounds.h" - -#include - -namespace llvm { -namespace orc { -namespace shared { - -/// Base class of all fatal RPC errors (those that necessarily result in the -/// termination of the RPC session). -class RPCFatalError : public ErrorInfo { -public: - static char ID; -}; - -/// RPCConnectionClosed is returned from RPC operations if the RPC connection -/// has already been closed due to either an error or graceful disconnection. -class ConnectionClosed : public ErrorInfo { -public: - static char ID; - std::error_code convertToErrorCode() const override; - void log(raw_ostream &OS) const override; -}; - -/// BadFunctionCall is returned from handleOne when the remote makes a call with -/// an unrecognized function id. -/// -/// This error is fatal because Orc RPC needs to know how to parse a function -/// call to know where the next call starts, and if it doesn't recognize the -/// function id it cannot parse the call. -template -class BadFunctionCall - : public ErrorInfo, RPCFatalError> { -public: - static char ID; - - BadFunctionCall(FnIdT FnId, SeqNoT SeqNo) - : FnId(std::move(FnId)), SeqNo(std::move(SeqNo)) {} - - std::error_code convertToErrorCode() const override { - return orcError(OrcErrorCode::UnexpectedRPCCall); - } - - void log(raw_ostream &OS) const override { - OS << "Call to invalid RPC function id '" << FnId - << "' with " - "sequence number " - << SeqNo; - } - -private: - FnIdT FnId; - SeqNoT SeqNo; -}; - -template -char BadFunctionCall::ID = 0; - -/// InvalidSequenceNumberForResponse is returned from handleOne when a response -/// call arrives with a sequence number that doesn't correspond to any in-flight -/// function call. -/// -/// This error is fatal because Orc RPC needs to know how to parse the rest of -/// the response call to know where the next call starts, and if it doesn't have -/// a result parser for this sequence number it can't do that. -template -class InvalidSequenceNumberForResponse - : public ErrorInfo, - RPCFatalError> { -public: - static char ID; - - InvalidSequenceNumberForResponse(SeqNoT SeqNo) : SeqNo(std::move(SeqNo)) {} - - std::error_code convertToErrorCode() const override { - return orcError(OrcErrorCode::UnexpectedRPCCall); - }; - - void log(raw_ostream &OS) const override { - OS << "Response has unknown sequence number " << SeqNo; - } - -private: - SeqNoT SeqNo; -}; - -template -char InvalidSequenceNumberForResponse::ID = 0; - -/// This non-fatal error will be passed to asynchronous result handlers in place -/// of a result if the connection goes down before a result returns, or if the -/// function to be called cannot be negotiated with the remote. -class ResponseAbandoned : public ErrorInfo { -public: - static char ID; - - std::error_code convertToErrorCode() const override; - void log(raw_ostream &OS) const override; -}; - -/// This error is returned if the remote does not have a handler installed for -/// the given RPC function. -class CouldNotNegotiate : public ErrorInfo { -public: - static char ID; - - CouldNotNegotiate(std::string Signature); - std::error_code convertToErrorCode() const override; - void log(raw_ostream &OS) const override; - const std::string &getSignature() const { return Signature; } - -private: - std::string Signature; -}; - -template class RPCFunction; - -// RPC Function class. -// DerivedFunc should be a user defined class with a static 'getName()' method -// returning a const char* representing the function's name. -template -class RPCFunction { -public: - /// User defined function type. - using Type = RetT(ArgTs...); - - /// Return type. - using ReturnType = RetT; - - /// Returns the full function prototype as a string. - static const char *getPrototype() { - static std::string Name = [] { - std::string Name; - raw_string_ostream(Name) - << SerializationTypeName::getName() << " " - << DerivedFunc::getName() << "(" - << SerializationTypeNameSequence() << ")"; - return Name; - }(); - return Name.data(); - } -}; - -/// Allocates RPC function ids during autonegotiation. -/// Specializations of this class must provide four members: -/// -/// static T getInvalidId(): -/// Should return a reserved id that will be used to represent missing -/// functions during autonegotiation. -/// -/// static T getResponseId(): -/// Should return a reserved id that will be used to send function responses -/// (return values). -/// -/// static T getNegotiateId(): -/// Should return a reserved id for the negotiate function, which will be used -/// to negotiate ids for user defined functions. -/// -/// template T allocate(): -/// Allocate a unique id for function Func. -template class RPCFunctionIdAllocator; - -/// This specialization of RPCFunctionIdAllocator provides a default -/// implementation for integral types. -template -class RPCFunctionIdAllocator::value>> { -public: - static T getInvalidId() { return T(0); } - static T getResponseId() { return T(1); } - static T getNegotiateId() { return T(2); } - - template T allocate() { return NextId++; } - -private: - T NextId = 3; -}; - -namespace detail { - -/// Provides a typedef for a tuple containing the decayed argument types. -template class RPCFunctionArgsTuple; - -template -class RPCFunctionArgsTuple { -public: - using Type = std::tuple>...>; -}; - -// ResultTraits provides typedefs and utilities specific to the return type -// of functions. -template class ResultTraits { -public: - // The return type wrapped in llvm::Expected. - using ErrorReturnType = Expected; - -#ifdef _MSC_VER - // The ErrorReturnType wrapped in a std::promise. - using ReturnPromiseType = std::promise>; - - // The ErrorReturnType wrapped in a std::future. - using ReturnFutureType = std::future>; -#else - // The ErrorReturnType wrapped in a std::promise. - using ReturnPromiseType = std::promise; - - // The ErrorReturnType wrapped in a std::future. - using ReturnFutureType = std::future; -#endif - - // Create a 'blank' value of the ErrorReturnType, ready and safe to - // overwrite. - static ErrorReturnType createBlankErrorReturnValue() { - return ErrorReturnType(RetT()); - } - - // Consume an abandoned ErrorReturnType. - static void consumeAbandoned(ErrorReturnType RetOrErr) { - consumeError(RetOrErr.takeError()); - } - - static ErrorReturnType returnError(Error Err) { return std::move(Err); } -}; - -// ResultTraits specialization for void functions. -template <> class ResultTraits { -public: - // For void functions, ErrorReturnType is llvm::Error. - using ErrorReturnType = Error; - -#ifdef _MSC_VER - // The ErrorReturnType wrapped in a std::promise. - using ReturnPromiseType = std::promise; - - // The ErrorReturnType wrapped in a std::future. - using ReturnFutureType = std::future; -#else - // The ErrorReturnType wrapped in a std::promise. - using ReturnPromiseType = std::promise; - - // The ErrorReturnType wrapped in a std::future. - using ReturnFutureType = std::future; -#endif - - // Create a 'blank' value of the ErrorReturnType, ready and safe to - // overwrite. - static ErrorReturnType createBlankErrorReturnValue() { - return ErrorReturnType::success(); - } - - // Consume an abandoned ErrorReturnType. - static void consumeAbandoned(ErrorReturnType Err) { - consumeError(std::move(Err)); - } - - static ErrorReturnType returnError(Error Err) { return Err; } -}; - -// ResultTraits is equivalent to ResultTraits. This allows -// handlers for void RPC functions to return either void (in which case they -// implicitly succeed) or Error (in which case their error return is -// propagated). See usage in HandlerTraits::runHandlerHelper. -template <> class ResultTraits : public ResultTraits {}; - -// ResultTraits> is equivalent to ResultTraits. This allows -// handlers for RPC functions returning a T to return either a T (in which -// case they implicitly succeed) or Expected (in which case their error -// return is propagated). See usage in HandlerTraits::runHandlerHelper. -template -class ResultTraits> : public ResultTraits {}; - -// Determines whether an RPC function's defined error return type supports -// error return value. -template class SupportsErrorReturn { -public: - static const bool value = false; -}; - -template <> class SupportsErrorReturn { -public: - static const bool value = true; -}; - -template class SupportsErrorReturn> { -public: - static const bool value = true; -}; - -// RespondHelper packages return values based on whether or not the declared -// RPC function return type supports error returns. -template class RespondHelper; - -// RespondHelper specialization for functions that support error returns. -template <> class RespondHelper { -public: - // Send Expected. - template - static Error sendResult(ChannelT &C, const FunctionIdT &ResponseId, - SequenceNumberT SeqNo, - Expected ResultOrErr) { - if (!ResultOrErr && ResultOrErr.template errorIsA()) - return ResultOrErr.takeError(); - - // Open the response message. - if (auto Err = C.startSendMessage(ResponseId, SeqNo)) - return Err; - - // Serialize the result. - if (auto Err = - SerializationTraits>:: - serialize(C, std::move(ResultOrErr))) - return Err; - - // Close the response message. - if (auto Err = C.endSendMessage()) - return Err; - return C.send(); - } - - template - static Error sendResult(ChannelT &C, const FunctionIdT &ResponseId, - SequenceNumberT SeqNo, Error Err) { - if (Err && Err.isA()) - return Err; - if (auto Err2 = C.startSendMessage(ResponseId, SeqNo)) - return Err2; - if (auto Err2 = serializeSeq(C, std::move(Err))) - return Err2; - if (auto Err2 = C.endSendMessage()) - return Err2; - return C.send(); - } -}; - -// RespondHelper specialization for functions that do not support error returns. -template <> class RespondHelper { -public: - template - static Error sendResult(ChannelT &C, const FunctionIdT &ResponseId, - SequenceNumberT SeqNo, - Expected ResultOrErr) { - if (auto Err = ResultOrErr.takeError()) - return Err; - - // Open the response message. - if (auto Err = C.startSendMessage(ResponseId, SeqNo)) - return Err; - - // Serialize the result. - if (auto Err = - SerializationTraits::serialize( - C, *ResultOrErr)) - return Err; - - // End the response message. - if (auto Err = C.endSendMessage()) - return Err; - - return C.send(); - } - - template - static Error sendResult(ChannelT &C, const FunctionIdT &ResponseId, - SequenceNumberT SeqNo, Error Err) { - if (Err) - return Err; - if (auto Err2 = C.startSendMessage(ResponseId, SeqNo)) - return Err2; - if (auto Err2 = C.endSendMessage()) - return Err2; - return C.send(); - } -}; - -// Send a response of the given wire return type (WireRetT) over the -// channel, with the given sequence number. -template -Error respond(ChannelT &C, const FunctionIdT &ResponseId, SequenceNumberT SeqNo, - Expected ResultOrErr) { - return RespondHelper::value>:: - template sendResult(C, ResponseId, SeqNo, - std::move(ResultOrErr)); -} - -// Send an empty response message on the given channel to indicate that -// the handler ran. -template -Error respond(ChannelT &C, const FunctionIdT &ResponseId, SequenceNumberT SeqNo, - Error Err) { - return RespondHelper::value>::sendResult( - C, ResponseId, SeqNo, std::move(Err)); -} - -// Converts a given type to the equivalent error return type. -template class WrappedHandlerReturn { -public: - using Type = Expected; -}; - -template class WrappedHandlerReturn> { -public: - using Type = Expected; -}; - -template <> class WrappedHandlerReturn { -public: - using Type = Error; -}; - -template <> class WrappedHandlerReturn { -public: - using Type = Error; -}; - -template <> class WrappedHandlerReturn { -public: - using Type = Error; -}; - -// Traits class that strips the response function from the list of handler -// arguments. -template class AsyncHandlerTraits; - -template -class AsyncHandlerTraits)>, - ArgTs...)> { -public: - using Type = Error(ArgTs...); - using ResultType = Expected; -}; - -template -class AsyncHandlerTraits, ArgTs...)> { -public: - using Type = Error(ArgTs...); - using ResultType = Error; -}; - -template -class AsyncHandlerTraits, ArgTs...)> { -public: - using Type = Error(ArgTs...); - using ResultType = Error; -}; - -template -class AsyncHandlerTraits, ArgTs...)> { -public: - using Type = Error(ArgTs...); - using ResultType = Error; -}; - -template -class AsyncHandlerTraits - : public AsyncHandlerTraits, - ArgTs...)> {}; - -// This template class provides utilities related to RPC function handlers. -// The base case applies to non-function types (the template class is -// specialized for function types) and inherits from the appropriate -// speciilization for the given non-function type's call operator. -template -class HandlerTraits - : public HandlerTraits< - decltype(&std::remove_reference::type::operator())> {}; - -// Traits for handlers with a given function type. -template -class HandlerTraits { -public: - // Function type of the handler. - using Type = RetT(ArgTs...); - - // Return type of the handler. - using ReturnType = RetT; - - // Call the given handler with the given arguments. - template - static typename WrappedHandlerReturn::Type - unpackAndRun(HandlerT &Handler, std::tuple &Args) { - return unpackAndRunHelper(Handler, Args, - std::index_sequence_for()); - } - - // Call the given handler with the given arguments. - template - static Error unpackAndRunAsync(HandlerT &Handler, ResponderT &Responder, - std::tuple &Args) { - return unpackAndRunAsyncHelper(Handler, Responder, Args, - std::index_sequence_for()); - } - - // Call the given handler with the given arguments. - template - static std::enable_if_t< - std::is_void::ReturnType>::value, Error> - run(HandlerT &Handler, ArgTs &&...Args) { - Handler(std::move(Args)...); - return Error::success(); - } - - template - static std::enable_if_t< - !std::is_void::ReturnType>::value, - typename HandlerTraits::ReturnType> - run(HandlerT &Handler, TArgTs... Args) { - return Handler(std::move(Args)...); - } - - // Serialize arguments to the channel. - template - static Error serializeArgs(ChannelT &C, const CArgTs... CArgs) { - return SequenceSerialization::serialize(C, CArgs...); - } - - // Deserialize arguments from the channel. - template - static Error deserializeArgs(ChannelT &C, std::tuple &Args) { - return deserializeArgsHelper(C, Args, std::index_sequence_for()); - } - -private: - template - static Error deserializeArgsHelper(ChannelT &C, std::tuple &Args, - std::index_sequence _) { - return SequenceSerialization::deserialize( - C, std::get(Args)...); - } - - template - static typename WrappedHandlerReturn< - typename HandlerTraits::ReturnType>::Type - unpackAndRunHelper(HandlerT &Handler, ArgTuple &Args, - std::index_sequence) { - return run(Handler, std::move(std::get(Args))...); - } - - template - static typename WrappedHandlerReturn< - typename HandlerTraits::ReturnType>::Type - unpackAndRunAsyncHelper(HandlerT &Handler, ResponderT &Responder, - ArgTuple &Args, std::index_sequence) { - return run(Handler, Responder, std::move(std::get(Args))...); - } -}; - -// Handler traits for free functions. -template -class HandlerTraits : public HandlerTraits { -}; - -// Handler traits for class methods (especially call operators for lambdas). -template -class HandlerTraits - : public HandlerTraits {}; - -// Handler traits for const class methods (especially call operators for -// lambdas). -template -class HandlerTraits - : public HandlerTraits {}; - -// Utility to peel the Expected wrapper off a response handler error type. -template class ResponseHandlerArg; - -template class ResponseHandlerArg)> { -public: - using ArgType = Expected; - using UnwrappedArgType = ArgT; -}; - -template -class ResponseHandlerArg)> { -public: - using ArgType = Expected; - using UnwrappedArgType = ArgT; -}; - -template <> class ResponseHandlerArg { -public: - using ArgType = Error; -}; - -template <> class ResponseHandlerArg { -public: - using ArgType = Error; -}; - -// ResponseHandler represents a handler for a not-yet-received function call -// result. -template class ResponseHandler { -public: - virtual ~ResponseHandler() {} - - // Reads the function result off the wire and acts on it. The meaning of - // "act" will depend on how this method is implemented in any given - // ResponseHandler subclass but could, for example, mean running a - // user-specified handler or setting a promise value. - virtual Error handleResponse(ChannelT &C) = 0; - - // Abandons this outstanding result. - virtual void abandon() = 0; - - // Create an error instance representing an abandoned response. - static Error createAbandonedResponseError() { - return make_error(); - } -}; - -// ResponseHandler subclass for RPC functions with non-void returns. -template -class ResponseHandlerImpl : public ResponseHandler { -public: - ResponseHandlerImpl(HandlerT Handler) : Handler(std::move(Handler)) {} - - // Handle the result by deserializing it from the channel then passing it - // to the user defined handler. - Error handleResponse(ChannelT &C) override { - using UnwrappedArgType = typename ResponseHandlerArg< - typename HandlerTraits::Type>::UnwrappedArgType; - UnwrappedArgType Result; - if (auto Err = - SerializationTraits::deserialize(C, Result)) - return Err; - if (auto Err = C.endReceiveMessage()) - return Err; - return Handler(std::move(Result)); - } - - // Abandon this response by calling the handler with an 'abandoned response' - // error. - void abandon() override { - if (auto Err = Handler(this->createAbandonedResponseError())) { - // Handlers should not fail when passed an abandoned response error. - report_fatal_error(std::move(Err)); - } - } - -private: - HandlerT Handler; -}; - -// ResponseHandler subclass for RPC functions with void returns. -template -class ResponseHandlerImpl - : public ResponseHandler { -public: - ResponseHandlerImpl(HandlerT Handler) : Handler(std::move(Handler)) {} - - // Handle the result (no actual value, just a notification that the function - // has completed on the remote end) by calling the user-defined handler with - // Error::success(). - Error handleResponse(ChannelT &C) override { - if (auto Err = C.endReceiveMessage()) - return Err; - return Handler(Error::success()); - } - - // Abandon this response by calling the handler with an 'abandoned response' - // error. - void abandon() override { - if (auto Err = Handler(this->createAbandonedResponseError())) { - // Handlers should not fail when passed an abandoned response error. - report_fatal_error(std::move(Err)); - } - } - -private: - HandlerT Handler; -}; - -template -class ResponseHandlerImpl, HandlerT> - : public ResponseHandler { -public: - ResponseHandlerImpl(HandlerT Handler) : Handler(std::move(Handler)) {} - - // Handle the result by deserializing it from the channel then passing it - // to the user defined handler. - Error handleResponse(ChannelT &C) override { - using HandlerArgType = typename ResponseHandlerArg< - typename HandlerTraits::Type>::ArgType; - HandlerArgType Result((typename HandlerArgType::value_type())); - - if (auto Err = SerializationTraits, - HandlerArgType>::deserialize(C, Result)) - return Err; - if (auto Err = C.endReceiveMessage()) - return Err; - return Handler(std::move(Result)); - } - - // Abandon this response by calling the handler with an 'abandoned response' - // error. - void abandon() override { - if (auto Err = Handler(this->createAbandonedResponseError())) { - // Handlers should not fail when passed an abandoned response error. - report_fatal_error(std::move(Err)); - } - } - -private: - HandlerT Handler; -}; - -template -class ResponseHandlerImpl - : public ResponseHandler { -public: - ResponseHandlerImpl(HandlerT Handler) : Handler(std::move(Handler)) {} - - // Handle the result by deserializing it from the channel then passing it - // to the user defined handler. - Error handleResponse(ChannelT &C) override { - Error Result = Error::success(); - if (auto Err = SerializationTraits::deserialize( - C, Result)) { - consumeError(std::move(Result)); - return Err; - } - if (auto Err = C.endReceiveMessage()) { - consumeError(std::move(Result)); - return Err; - } - return Handler(std::move(Result)); - } - - // Abandon this response by calling the handler with an 'abandoned response' - // error. - void abandon() override { - if (auto Err = Handler(this->createAbandonedResponseError())) { - // Handlers should not fail when passed an abandoned response error. - report_fatal_error(std::move(Err)); - } - } - -private: - HandlerT Handler; -}; - -// Create a ResponseHandler from a given user handler. -template -std::unique_ptr> createResponseHandler(HandlerT H) { - return std::make_unique>( - std::move(H)); -} - -// Helper for wrapping member functions up as functors. This is useful for -// installing methods as result handlers. -template -class MemberFnWrapper { -public: - using MethodT = RetT (ClassT::*)(ArgTs...); - MemberFnWrapper(ClassT &Instance, MethodT Method) - : Instance(Instance), Method(Method) {} - RetT operator()(ArgTs &&...Args) { - return (Instance.*Method)(std::move(Args)...); - } - -private: - ClassT &Instance; - MethodT Method; -}; - -// Helper that provides a Functor for deserializing arguments. -template class ReadArgs { -public: - Error operator()() { return Error::success(); } -}; - -template -class ReadArgs : public ReadArgs { -public: - ReadArgs(ArgT &Arg, ArgTs &...Args) : ReadArgs(Args...), Arg(Arg) {} - - Error operator()(ArgT &ArgVal, ArgTs &...ArgVals) { - this->Arg = std::move(ArgVal); - return ReadArgs::operator()(ArgVals...); - } - -private: - ArgT &Arg; -}; - -// Manage sequence numbers. -template class SequenceNumberManager { -public: - // Reset, making all sequence numbers available. - void reset() { - std::lock_guard Lock(SeqNoLock); - NextSequenceNumber = 0; - FreeSequenceNumbers.clear(); - } - - // Get the next available sequence number. Will re-use numbers that have - // been released. - SequenceNumberT getSequenceNumber() { - std::lock_guard Lock(SeqNoLock); - if (FreeSequenceNumbers.empty()) - return NextSequenceNumber++; - auto SequenceNumber = FreeSequenceNumbers.back(); - FreeSequenceNumbers.pop_back(); - return SequenceNumber; - } - - // Release a sequence number, making it available for re-use. - void releaseSequenceNumber(SequenceNumberT SequenceNumber) { - std::lock_guard Lock(SeqNoLock); - FreeSequenceNumbers.push_back(SequenceNumber); - } - -private: - std::mutex SeqNoLock; - SequenceNumberT NextSequenceNumber = 0; - std::vector FreeSequenceNumbers; -}; - -// Checks that predicate P holds for each corresponding pair of type arguments -// from T1 and T2 tuple. -template