Index: llvm/include/llvm/IR/Attributes.h =================================================================== --- llvm/include/llvm/IR/Attributes.h +++ llvm/include/llvm/IR/Attributes.h @@ -107,6 +107,10 @@ const Optional &NumElemsArg); static Attribute getWithByValType(LLVMContext &Context, Type *Ty); + static Attribute::AttrKind getAttrKindFromName(StringRef AttrName); + + static StringRef getNameFromAttrKind(Attribute::AttrKind AttrKind); + //===--------------------------------------------------------------------===// // Attribute Accessors //===--------------------------------------------------------------------===// Index: llvm/include/llvm/Transforms/Utils/AssumeBuilder.h =================================================================== --- /dev/null +++ llvm/include/llvm/Transforms/Utils/AssumeBuilder.h @@ -0,0 +1,79 @@ +//===- AssumeBuilder.h - Build assume to preserve informations---*- 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_TRANSFORMS_UTILS_ASSUMEBUILDER_H +#define LLVM_TRANSFORMS_UTILS_ASSUMEBUILDER_H + +#include "llvm/ADT/SmallSet.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/PassManager.h" + +namespace llvm { + +namespace detail_assume_builder { + +struct builderState { + Module *M; + + SmallSet, 8> BundleSet; + +public: + builderState(Module *M) : M(M) {} + Instruction *Build(); + void AddInstruction(const Instruction *I); +}; + +} // namespace detail_assume_builder + +/// Build an instruction to preserve informations that can be derived from +/// this call. if no information was found, returns null. +Instruction *buildAssumeFromInst(const Instruction *I, Module *M) { + detail_assume_builder::builderState Builder(M); + Builder.AddInstruction(I); + return Builder.Build(); +} + +Instruction *buildAssumeFromInst(Instruction *I) { + return buildAssumeFromInst(I, I->getModule()); +} + +/// Build an instruction to preserve informations that can be derived +/// from a range of instructions. if no information was found, returns null. +template Instruction *buildAssumeFromRange(Range &&R) { + if (R.begin() == R.end()) + return nullptr; + return buildAssumeFromRange(R, (*R.begin())->getModule()); +} + +template +Instruction *buildAssumeFromRange(Range &&R, Module *M) { + detail_assume_builder::builderState Builder(M); + for (const Instruction &I : R) + Builder.AddInstruction(&I); + return Builder.Build(); +} + +/// This pass will print the current module with an llvm.assume containing all +/// the information is was able to salvage from the following instruction. +/// The same process can be done at the BasicBlock level with the option +/// GroupAssumeBuilderPrinter. +/// it main use is testing. +class AssumeBuilderPrinterPass + : public PassInfoMixin { + raw_ostream &OS; + +public: + explicit AssumeBuilderPrinterPass(raw_ostream &OS) : OS(OS) {} + + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); +}; + +} // namespace llvm + +#endif Index: llvm/lib/IR/Attributes.cpp =================================================================== --- llvm/lib/IR/Attributes.cpp +++ llvm/lib/IR/Attributes.cpp @@ -176,6 +176,29 @@ return get(Context, AllocSize, packAllocSizeArgs(ElemSizeArg, NumElemsArg)); } +Attribute::AttrKind Attribute::getAttrKindFromName(StringRef AttrName) { + return StringSwitch(AttrName) +#define GET_ATTR_NAMES +#define ATTRIBUTE_ENUM(ENUM_NAME, DISPLAY_NAME) \ + .Case(#DISPLAY_NAME, Attribute::ENUM_NAME) +#include "llvm/IR/Attributes.inc" + .Default(Attribute::None); +} + +StringRef Attribute::getNameFromAttrKind(Attribute::AttrKind AttrKind) { + switch (AttrKind) { +#define GET_ATTR_NAMES +#define ATTRIBUTE_ENUM(ENUM_NAME, DISPLAY_NAME) \ + case AttrKind::ENUM_NAME: \ + return #DISPLAY_NAME; +#include "llvm/IR/Attributes.inc" + case AttrKind::None: + return "none"; + default: + llvm_unreachable("invalid Kind"); + } +} + //===----------------------------------------------------------------------===// // Attribute Accessor Methods //===----------------------------------------------------------------------===// Index: llvm/lib/IR/Core.cpp =================================================================== --- llvm/lib/IR/Core.cpp +++ llvm/lib/IR/Core.cpp @@ -127,17 +127,8 @@ return LLVMGetMDKindIDInContext(LLVMGetGlobalContext(), Name, SLen); } -static Attribute::AttrKind getAttrKindFromName(StringRef AttrName) { - return StringSwitch(AttrName) -#define GET_ATTR_NAMES -#define ATTRIBUTE_ENUM(ENUM_NAME, DISPLAY_NAME) \ - .Case(#DISPLAY_NAME, Attribute::ENUM_NAME) -#include "llvm/IR/Attributes.inc" - .Default(Attribute::None); -} - unsigned LLVMGetEnumAttributeKindForName(const char *Name, size_t SLen) { - return getAttrKindFromName(StringRef(Name, SLen)); + return Attribute::getAttrKindFromName(StringRef(Name, SLen)); } unsigned LLVMGetLastEnumAttributeKind(void) { Index: llvm/lib/Passes/PassBuilder.cpp =================================================================== --- llvm/lib/Passes/PassBuilder.cpp +++ llvm/lib/Passes/PassBuilder.cpp @@ -168,6 +168,7 @@ #include "llvm/Transforms/Scalar/TailRecursionElimination.h" #include "llvm/Transforms/Scalar/WarnMissedTransforms.h" #include "llvm/Transforms/Utils/AddDiscriminators.h" +#include "llvm/Transforms/Utils/AssumeBuilder.h" #include "llvm/Transforms/Utils/BreakCriticalEdges.h" #include "llvm/Transforms/Utils/CanonicalizeAliases.h" #include "llvm/Transforms/Utils/EntryExitInstrumenter.h" Index: llvm/lib/Passes/PassRegistry.def =================================================================== --- llvm/lib/Passes/PassRegistry.def +++ llvm/lib/Passes/PassRegistry.def @@ -212,6 +212,7 @@ FUNCTION_PASS("pgo-memop-opt", PGOMemOPSizeOpt()) FUNCTION_PASS("print", PrintFunctionPass(dbgs())) FUNCTION_PASS("print", AssumptionPrinterPass(dbgs())) +FUNCTION_PASS("print", AssumeBuilderPrinterPass(dbgs())) FUNCTION_PASS("print", BlockFrequencyPrinterPass(dbgs())) FUNCTION_PASS("print", BranchProbabilityPrinterPass(dbgs())) FUNCTION_PASS("print", DependenceAnalysisPrinterPass(dbgs())) Index: llvm/lib/Transforms/Utils/AssumeBuilder.cpp =================================================================== --- /dev/null +++ llvm/lib/Transforms/Utils/AssumeBuilder.cpp @@ -0,0 +1,115 @@ +//===- AssumeBuilder.h - Build assume to preserve informations --*- 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 +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Utils/AssumeBuilder.h" +#include "llvm/IR/AssemblyAnnotationWriter.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FormattedStream.h" + +using namespace llvm; +using namespace detail_assume_builder; + +/// This option will cause the printer passe to generate one llvm.assume per +/// basic block with all the information of that basic block instead of one per +/// instruction with information of that instruction. +cl::opt GroupAssumeBuilderPrinter("assume-builder-printer-group", + cl::ReallyHidden, cl::init(false)); + +static void AddAttribute(builderState &Builder, Attribute Attr, Value *WasOn) { + StringRef Name; + Value *AttrArg = nullptr; + if (Attr.isStringAttribute()) + Name = Attr.getKindAsString(); + else + Name = Attribute::getNameFromAttrKind(Attr.getKindAsEnum()); + if (Attr.isIntAttribute()) + AttrArg = ConstantInt::get(Type::getInt64Ty(Builder.M->getContext()), + Attr.getValueAsInt()); + Builder.BundleSet.insert(std::make_tuple(Name.data(), AttrArg, WasOn)); +} + +static void AddCall(builderState &Builder, const CallBase *Call) { + for (AttributeList AttrList : + {Call->getAttributes(), Call->getCalledFunction()->getAttributes()}) { + /// the start Index is at 1 because 0 is the return value. + for (unsigned Idx = 1; Idx < AttrList.getNumAttrSets(); Idx++) + for (Attribute Attr : AttrList.getAttributes(Idx)) + AddAttribute(Builder, Attr, Call->getArgOperand(Idx - 1)); + for (Attribute Attr : AttrList.getFnAttributes()) + AddAttribute(Builder, Attr, Call->getCalledFunction()); + } +} + +Instruction *builderState::Build() { + if (!BundleSet.empty()) { + Function *FnAssume = Intrinsic::getDeclaration(M, Intrinsic::assume); + LLVMContext &C = M->getContext(); + SmallVector OpBundle; + for (auto Elem : BundleSet) { + SmallVector Args; + if (std::get<1>(Elem)) + Args.push_back(std::get<1>(Elem)); + if (std::get<2>(Elem)) + Args.push_back(std::get<2>(Elem)); + OpBundle.push_back(OperandBundleDefT(std::get<0>(Elem), Args)); + } + CallInst *Res = CallInst::Create( + FnAssume, ArrayRef({ConstantInt::getTrue(C)}), OpBundle); + return Res; + } + return nullptr; +} + +void builderState::AddInstruction(const Instruction *I) { + if (auto *Call = dyn_cast(I)) + AddCall(*this, Call); +} + +namespace { + +class AssumeWriter final : public AssemblyAnnotationWriter { + Module *M; + +public: + AssumeWriter(Module *M) : M(M) {} + + static void Emit(Instruction *I, formatted_raw_ostream &OS) { + I->print(OS); + OS << " \n"; + /// Remove all operands so the I can be deleted properly as it is + /// not inserted. + for (unsigned op = 0, e = I->getNumOperands(); op != e; ++op) + I->setOperand(op, nullptr); + } + + virtual void emitInstructionAnnot(const Instruction *I, + formatted_raw_ostream &OS) override { + if (!GroupAssumeBuilderPrinter) + if (Instruction *Assume = buildAssumeFromInst(I, M)) + Emit(Assume, OS); + } + + virtual void emitBasicBlockStartAnnot(const BasicBlock *BB, + formatted_raw_ostream &OS) override { + if (GroupAssumeBuilderPrinter) + if (Instruction *Assume = buildAssumeFromRange(*BB, M)) + Emit(Assume, OS); + } +}; + +} // namespace + +PreservedAnalyses AssumeBuilderPrinterPass::run(Function &F, + FunctionAnalysisManager &AM) { + AssumeWriter Writer(F.getParent()); + F.print(OS, &Writer); + return PreservedAnalyses::all(); +} Index: llvm/lib/Transforms/Utils/CMakeLists.txt =================================================================== --- llvm/lib/Transforms/Utils/CMakeLists.txt +++ llvm/lib/Transforms/Utils/CMakeLists.txt @@ -1,6 +1,7 @@ add_llvm_component_library(LLVMTransformUtils ASanStackFrameLayout.cpp AddDiscriminators.cpp + AssumeBuilder.cpp BasicBlockUtils.cpp BreakCriticalEdges.cpp BuildLibCalls.cpp Index: llvm/test/Transforms/Util/assume-builder.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/Util/assume-builder.ll @@ -0,0 +1,55 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -passes='print' -disable-output %s 2>&1 | FileCheck %s --check-prefixes=CHECK +; RUN: opt -passes='print' --assume-builder-printer-group -disable-output %s 2>&1 | FileCheck %s --check-prefixes=GROUPE + +declare void @func(i32*, i32*) +declare void @func_cold(i32*) cold +declare void @func_strbool(i32*) "no-jump-tables" +declare void @func_many(i32*) "no-jump-tables" nounwind "less-precise-fpmad" willreturn norecurse +declare void @func_argattr(i32* align 8, i32* nonnull) nounwind + +define void @test(i32* %P, i32* %P1, i32* %P2, i32* %P3) { +; CHECK-LABEL: @test( +; CHECK-NEXT: call addrspace(0) void @llvm.assume(i1 true) [ "nonnull"(i32* [[P:%.*]]), "dereferenceable"(i64 16, i32* [[P]]) ] +; CHECK-NEXT: call void @func(i32* nonnull dereferenceable(16) [[P]], i32* null) +; CHECK-NEXT: call addrspace(0) void @llvm.assume(i1 true) [ "dereferenceable"(i64 12, i32* [[P1:%.*]]), "nonnull"(i32* [[P]]) ] +; CHECK-NEXT: call void @func(i32* dereferenceable(12) [[P1]], i32* nonnull [[P]]) +; CHECK-NEXT: call addrspace(0) void @llvm.assume(i1 true) [ "dereferenceable"(i64 12, i32* [[P1]]), "cold"(void (i32*)* @func_cold) ] +; CHECK-NEXT: call void @func_cold(i32* dereferenceable(12) [[P1]]) #0 +; CHECK-NEXT: call addrspace(0) void @llvm.assume(i1 true) [ "dereferenceable"(i64 12, i32* [[P1]]), "cold"(void (i32*)* @func_cold) ] +; CHECK-NEXT: call void @func_cold(i32* dereferenceable(12) [[P1]]) +; CHECK-NEXT: call void @func(i32* [[P1]], i32* [[P]]) +; CHECK-NEXT: call addrspace(0) void @llvm.assume(i1 true) [ "no-jump-tables"(void (i32*)* @func_strbool) ] +; CHECK-NEXT: call void @func_strbool(i32* [[P1]]) +; CHECK-NEXT: call addrspace(0) void @llvm.assume(i1 true) [ "dereferenceable"(i64 16, i32* [[P]]), "dereferenceable"(i64 8, i32* [[P]]) ] +; CHECK-NEXT: call void @func(i32* dereferenceable(16) [[P]], i32* dereferenceable(8) [[P]]) +; CHECK-NEXT: call addrspace(0) void @llvm.assume(i1 true) [ "align"(i64 8, i32* [[P1]]), "norecurse"(void (i32*)* @func_many), "nounwind"(void (i32*)* @func_many), "willreturn"(void (i32*)* @func_many), "less-precise-fpmad"(void (i32*)* @func_many), "no-jump-tables"(void (i32*)* @func_many) ] +; CHECK-NEXT: call void @func_many(i32* align 8 [[P1]]) +; CHECK-NEXT: call addrspace(0) void @llvm.assume(i1 true) [ "align"(i64 8, i32* [[P2:%.*]]), "nonnull"(i32* [[P3:%.*]]), "nounwind"(void (i32*, i32*)* @func_argattr) ] +; CHECK-NEXT: call void @func_argattr(i32* [[P2]], i32* [[P3]]) +; CHECK-NEXT: ret void +; +; GROUPE-LABEL: @test( +; GROUPE-NEXT: call addrspace(0) void @llvm.assume(i1 true) [ "no-jump-tables"(void (i32*)* @func_strbool), "no-jump-tables"(void (i32*)* @func_many), "less-precise-fpmad"(void (i32*)* @func_many), "willreturn"(void (i32*)* @func_many), "align"(i64 8, i32* [[P1:%.*]]), "align"(i64 8, i32* [[P2:%.*]]), "nonnull"(i32* [[P:%.*]]), "nonnull"(i32* [[P3:%.*]]), "norecurse"(void (i32*)* @func_many), "dereferenceable"(i64 16, i32* [[P]]), "dereferenceable"(i64 12, i32* [[P1]]), "dereferenceable"(i64 8, i32* [[P]]), "nounwind"(void (i32*)* @func_many), "nounwind"(void (i32*, i32*)* @func_argattr), "cold"(void (i32*)* @func_cold) ] +; GROUPE-NEXT: call void @func(i32* nonnull dereferenceable(16) [[P]], i32* null) +; GROUPE-NEXT: call void @func(i32* dereferenceable(12) [[P1]], i32* nonnull [[P]]) +; GROUPE-NEXT: call void @func_cold(i32* dereferenceable(12) [[P1]]) #0 +; GROUPE-NEXT: call void @func_cold(i32* dereferenceable(12) [[P1]]) +; GROUPE-NEXT: call void @func(i32* [[P1]], i32* [[P]]) +; GROUPE-NEXT: call void @func_strbool(i32* [[P1]]) +; GROUPE-NEXT: call void @func(i32* dereferenceable(16) [[P]], i32* dereferenceable(8) [[P]]) +; GROUPE-NEXT: call void @func_many(i32* align 8 [[P1]]) +; GROUPE-NEXT: call void @func_argattr(i32* [[P2]], i32* [[P3]]) +; GROUPE-NEXT: ret void +; + call void @func(i32* nonnull dereferenceable(16) %P, i32* null) + call void @func(i32* dereferenceable(12) %P1, i32* nonnull %P) + call void @func_cold(i32* dereferenceable(12) %P1) cold + call void @func_cold(i32* dereferenceable(12) %P1) + call void @func(i32* %P1, i32* %P) + call void @func_strbool(i32* %P1) + call void @func(i32* dereferenceable(16) %P, i32* dereferenceable(8) %P) + call void @func_many(i32* align 8 %P1) + call void @func_argattr(i32* %P2, i32* %P3) + ret void +}