Skip to content

Commit a7bbe45

Browse files
committed
Build assume from call
Fix attempt this is part of the implementation of http://lists.llvm.org/pipermail/llvm-dev/2019-December/137632.html this patch gives the basis of building an assume to preserve all information from an instruction and add support for building an assume that preserve the information from a call.
1 parent bf70494 commit a7bbe45

File tree

9 files changed

+302
-10
lines changed

9 files changed

+302
-10
lines changed

llvm/include/llvm/IR/Attributes.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ class Attribute {
107107
const Optional<unsigned> &NumElemsArg);
108108
static Attribute getWithByValType(LLVMContext &Context, Type *Ty);
109109

110+
static Attribute::AttrKind getAttrKindFromName(StringRef AttrName);
111+
112+
static StringRef getNameFromAttrKind(Attribute::AttrKind AttrKind);
113+
110114
//===--------------------------------------------------------------------===//
111115
// Attribute Accessors
112116
//===--------------------------------------------------------------------===//
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//===- KnowledgeRetention.h - utilities to preserve informations *- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file contain tools to preserve informations. They should be used before
10+
// performing a transformation that may move and delete instructions as those
11+
// transformation may destroy or worsen information that can be derived from the
12+
// IR.
13+
//
14+
//===----------------------------------------------------------------------===//
15+
16+
#ifndef LLVM_TRANSFORMS_UTILS_ASSUMEBUILDER_H
17+
#define LLVM_TRANSFORMS_UTILS_ASSUMEBUILDER_H
18+
19+
#include "llvm/IR/Instruction.h"
20+
#include "llvm/IR/PassManager.h"
21+
22+
namespace llvm {
23+
24+
/// Build a call to llvm.assume to preserve informations that can be derived
25+
/// from the given instruction.
26+
/// If no information derived from \p I, this call returns null.
27+
/// The returned instruction is not inserted anywhere.
28+
CallInst *BuildAssumeFromInst(const Instruction *I, Module *M);
29+
inline CallInst *BuildAssumeFromInst(Instruction *I) {
30+
return BuildAssumeFromInst(I, I->getModule());
31+
}
32+
33+
/// This pass will try to build an llvm.assume for every instruction in the
34+
/// function. Its main purpose is testing.
35+
struct AssumeBuilderPass : public PassInfoMixin<AssumeBuilderPass> {
36+
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
37+
};
38+
39+
} // namespace llvm
40+
41+
#endif

llvm/lib/IR/Attributes.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,29 @@ Attribute::getWithAllocSizeArgs(LLVMContext &Context, unsigned ElemSizeArg,
176176
return get(Context, AllocSize, packAllocSizeArgs(ElemSizeArg, NumElemsArg));
177177
}
178178

179+
Attribute::AttrKind Attribute::getAttrKindFromName(StringRef AttrName) {
180+
return StringSwitch<Attribute::AttrKind>(AttrName)
181+
#define GET_ATTR_NAMES
182+
#define ATTRIBUTE_ENUM(ENUM_NAME, DISPLAY_NAME) \
183+
.Case(#DISPLAY_NAME, Attribute::ENUM_NAME)
184+
#include "llvm/IR/Attributes.inc"
185+
.Default(Attribute::None);
186+
}
187+
188+
StringRef Attribute::getNameFromAttrKind(Attribute::AttrKind AttrKind) {
189+
switch (AttrKind) {
190+
#define GET_ATTR_NAMES
191+
#define ATTRIBUTE_ENUM(ENUM_NAME, DISPLAY_NAME) \
192+
case Attribute::ENUM_NAME: \
193+
return #DISPLAY_NAME;
194+
#include "llvm/IR/Attributes.inc"
195+
case Attribute::None:
196+
return "none";
197+
default:
198+
llvm_unreachable("invalid Kind");
199+
}
200+
}
201+
179202
//===----------------------------------------------------------------------===//
180203
// Attribute Accessor Methods
181204
//===----------------------------------------------------------------------===//

llvm/lib/IR/Core.cpp

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -127,17 +127,8 @@ unsigned LLVMGetMDKindID(const char *Name, unsigned SLen) {
127127
return LLVMGetMDKindIDInContext(LLVMGetGlobalContext(), Name, SLen);
128128
}
129129

130-
static Attribute::AttrKind getAttrKindFromName(StringRef AttrName) {
131-
return StringSwitch<Attribute::AttrKind>(AttrName)
132-
#define GET_ATTR_NAMES
133-
#define ATTRIBUTE_ENUM(ENUM_NAME, DISPLAY_NAME) \
134-
.Case(#DISPLAY_NAME, Attribute::ENUM_NAME)
135-
#include "llvm/IR/Attributes.inc"
136-
.Default(Attribute::None);
137-
}
138-
139130
unsigned LLVMGetEnumAttributeKindForName(const char *Name, size_t SLen) {
140-
return getAttrKindFromName(StringRef(Name, SLen));
131+
return Attribute::getAttrKindFromName(StringRef(Name, SLen));
141132
}
142133

143134
unsigned LLVMGetLastEnumAttributeKind(void) {

llvm/lib/Passes/PassBuilder.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@
173173
#include "llvm/Transforms/Utils/CanonicalizeAliases.h"
174174
#include "llvm/Transforms/Utils/EntryExitInstrumenter.h"
175175
#include "llvm/Transforms/Utils/InjectTLIMappings.h"
176+
#include "llvm/Transforms/Utils/KnowledgeRetention.h"
176177
#include "llvm/Transforms/Utils/LCSSA.h"
177178
#include "llvm/Transforms/Utils/LibCallsShrinkWrap.h"
178179
#include "llvm/Transforms/Utils/LoopSimplify.h"

llvm/lib/Passes/PassRegistry.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ FUNCTION_PASS("aa-eval", AAEvaluator())
160160
FUNCTION_PASS("adce", ADCEPass())
161161
FUNCTION_PASS("add-discriminators", AddDiscriminatorsPass())
162162
FUNCTION_PASS("aggressive-instcombine", AggressiveInstCombinePass())
163+
FUNCTION_PASS("assume-builder", AssumeBuilderPass())
163164
FUNCTION_PASS("alignment-from-assumptions", AlignmentFromAssumptionsPass())
164165
FUNCTION_PASS("bdce", BDCEPass())
165166
FUNCTION_PASS("bounds-checking", BoundsCheckingPass())

llvm/lib/Transforms/Utils/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ add_llvm_component_library(LLVMTransformUtils
2828
InjectTLIMappings.cpp
2929
InstructionNamer.cpp
3030
IntegerDivision.cpp
31+
KnowledgeRetention.cpp
3132
LCSSA.cpp
3233
LibCallsShrinkWrap.cpp
3334
Local.cpp
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
//===- KnowledgeRetention.h - utilities to preserve informations *- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "llvm/Transforms/Utils/KnowledgeRetention.h"
10+
#include "llvm/ADT/DenseSet.h"
11+
#include "llvm/IR/InstIterator.h"
12+
#include "llvm/IR/IntrinsicInst.h"
13+
#include "llvm/IR/Module.h"
14+
#include "llvm/Support/CommandLine.h"
15+
16+
using namespace llvm;
17+
18+
namespace {
19+
20+
cl::opt<bool> ShouldPreserveAllAttributes(
21+
"assume-preserve-all", cl::init(false), cl::Hidden,
22+
cl::desc("enable preservation of all attrbitues. even those that are "
23+
"unlikely to be usefull"));
24+
25+
struct AssumedKnowledge {
26+
const char *Name;
27+
Value *Argument;
28+
enum {
29+
None,
30+
Empty,
31+
Tombstone,
32+
};
33+
/// Contain the argument and a flag if needed.
34+
llvm::PointerIntPair<Value *, 2> WasOn;
35+
};
36+
37+
} // namespace
38+
39+
namespace llvm {
40+
41+
template <> struct DenseMapInfo<AssumedKnowledge> {
42+
static AssumedKnowledge getEmptyKey() {
43+
return {nullptr, nullptr, {nullptr, AssumedKnowledge::Empty}};
44+
}
45+
static AssumedKnowledge getTombstoneKey() {
46+
return {nullptr, nullptr, {nullptr, AssumedKnowledge::Tombstone}};
47+
}
48+
static unsigned getHashValue(const AssumedKnowledge &AK) {
49+
return hash_combine(AK.Name, AK.Argument, AK.WasOn.getPointer());
50+
}
51+
static bool isEqual(const AssumedKnowledge &LHS,
52+
const AssumedKnowledge &RHS) {
53+
return LHS.WasOn == RHS.WasOn && LHS.Name == RHS.Name &&
54+
LHS.Argument == RHS.Argument;
55+
}
56+
};
57+
58+
} // namespace llvm
59+
60+
namespace {
61+
62+
/// Deterministically compare OperandBundleDef.
63+
/// The ordering is:
64+
/// - by the name of the attribute, (doesn't change)
65+
/// - then by the Value of the argument, (doesn't change)
66+
/// - lastly by the Name of the current Value it WasOn. (may change)
67+
/// This order is deterministic and allows looking for the right kind of
68+
/// attribute with binary search. However finding the right WasOn needs to be
69+
/// done via linear search because values can get remplaced.
70+
bool isLowerOpBundle(const OperandBundleDef &LHS, const OperandBundleDef &RHS) {
71+
auto getTuple = [](const OperandBundleDef &Op) {
72+
return std::make_tuple(
73+
Op.getTag(),
74+
Op.input_size() < 2
75+
? 0
76+
: cast<ConstantInt>(*std::next(Op.input_begin()))->getZExtValue(),
77+
Op.input_size() < 1 ? StringRef("") : (*Op.input_begin())->getName());
78+
};
79+
return getTuple(LHS) < getTuple(RHS);
80+
}
81+
82+
/// This class contain all knowledge that have been gather while building an
83+
/// llvm.assume and the function to manipulate it.
84+
struct AssumeBuilderState {
85+
Module *M;
86+
87+
SmallDenseSet<AssumedKnowledge, 8> AssumedKnowledgeSet;
88+
89+
AssumeBuilderState(Module *M) : M(M) {}
90+
91+
void addAttribute(Attribute Attr, Value *WasOn) {
92+
StringRef Name;
93+
Value *AttrArg = nullptr;
94+
if (Attr.isStringAttribute())
95+
if (ShouldPreserveAllAttributes)
96+
Name = Attr.getKindAsString();
97+
else
98+
return;
99+
else
100+
Name = Attribute::getNameFromAttrKind(Attr.getKindAsEnum());
101+
if (Attr.isIntAttribute())
102+
AttrArg = ConstantInt::get(Type::getInt64Ty(M->getContext()),
103+
Attr.getValueAsInt());
104+
AssumedKnowledgeSet.insert(
105+
{Name.data(), AttrArg, {WasOn, AssumedKnowledge::None}});
106+
}
107+
108+
void addCall(const CallBase *Call) {
109+
auto addAttrList = [&](AttributeList AttrList) {
110+
for (unsigned Idx = AttributeList::FirstArgIndex;
111+
Idx < AttrList.getNumAttrSets(); Idx++)
112+
for (Attribute Attr : AttrList.getAttributes(Idx))
113+
addAttribute(Attr, Call->getArgOperand(Idx - 1));
114+
if (ShouldPreserveAllAttributes)
115+
for (Attribute Attr : AttrList.getFnAttributes())
116+
addAttribute(Attr, nullptr);
117+
};
118+
addAttrList(Call->getAttributes());
119+
if (Function *Fn = Call->getCalledFunction())
120+
addAttrList(Fn->getAttributes());
121+
}
122+
123+
CallInst *build() {
124+
if (AssumedKnowledgeSet.empty())
125+
return nullptr;
126+
Function *FnAssume = Intrinsic::getDeclaration(M, Intrinsic::assume);
127+
LLVMContext &C = M->getContext();
128+
SmallVector<OperandBundleDef, 8> OpBundle;
129+
for (const AssumedKnowledge &Elem : AssumedKnowledgeSet) {
130+
SmallVector<Value *, 2> Args;
131+
if (Elem.WasOn.getPointer())
132+
Args.push_back(Elem.WasOn.getPointer());
133+
if (Elem.Argument)
134+
Args.push_back(Elem.Argument);
135+
OpBundle.push_back(OperandBundleDefT<Value *>(Elem.Name, Args));
136+
}
137+
llvm::sort(OpBundle, isLowerOpBundle);
138+
return CallInst::Create(
139+
FnAssume, ArrayRef<Value *>({ConstantInt::getTrue(C)}), OpBundle);
140+
}
141+
142+
void addInstruction(const Instruction *I) {
143+
if (auto *Call = dyn_cast<CallBase>(I))
144+
addCall(Call);
145+
// TODO: Add support for the other Instructions.
146+
// TODO: Maybe we should look around and merge with other llvm.assume.
147+
}
148+
};
149+
150+
} // namespace
151+
152+
CallInst *llvm::BuildAssumeFromInst(const Instruction *I, Module *M) {
153+
AssumeBuilderState Builder(M);
154+
Builder.addInstruction(I);
155+
return Builder.build();
156+
}
157+
158+
PreservedAnalyses AssumeBuilderPass::run(Function &F,
159+
FunctionAnalysisManager &AM) {
160+
for (Instruction &I : instructions(F))
161+
if (Instruction *Assume = BuildAssumeFromInst(&I))
162+
Assume->insertBefore(&I);
163+
return PreservedAnalyses::all();
164+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2+
; RUN: opt -passes='assume-builder' -S %s | FileCheck %s --check-prefixes=BASIC
3+
; RUN: opt -passes='assume-builder' --assume-preserve-all -S %s | FileCheck %s --check-prefixes=ALL
4+
5+
declare void @func(i32*, i32*)
6+
declare void @func_cold(i32*) cold
7+
declare void @func_strbool(i32*) "no-jump-tables"
8+
declare void @func_many(i32*) "no-jump-tables" nounwind "less-precise-fpmad" willreturn norecurse
9+
declare void @func_argattr(i32* align 8, i32* nonnull) nounwind
10+
11+
define void @test(i32* %P, i32* %P1, i32* %P2, i32* %P3) {
12+
; BASIC-LABEL: @test(
13+
; BASIC-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P:%.*]], i64 16), "nonnull"(i32* [[P]]) ]
14+
; BASIC-NEXT: call void @func(i32* nonnull dereferenceable(16) [[P]], i32* null)
15+
; BASIC-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P1:%.*]], i64 12), "nonnull"(i32* [[P]]) ]
16+
; BASIC-NEXT: call void @func(i32* dereferenceable(12) [[P1]], i32* nonnull [[P]])
17+
; BASIC-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P1]], i64 12) ]
18+
; BASIC-NEXT: call void @func_cold(i32* dereferenceable(12) [[P1]]) #0
19+
; BASIC-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P1]], i64 12) ]
20+
; BASIC-NEXT: call void @func_cold(i32* dereferenceable(12) [[P1]])
21+
; BASIC-NEXT: call void @func(i32* [[P1]], i32* [[P]])
22+
; BASIC-NEXT: call void @func_strbool(i32* [[P1]])
23+
; BASIC-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P]], i64 8), "dereferenceable"(i32* [[P]], i64 16) ]
24+
; BASIC-NEXT: call void @func(i32* dereferenceable(16) [[P]], i32* dereferenceable(8) [[P]])
25+
; BASIC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P1]], i64 8) ]
26+
; BASIC-NEXT: call void @func_many(i32* align 8 [[P1]])
27+
; BASIC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P2:%.*]], i64 8), "nonnull"(i32* [[P3:%.*]]) ]
28+
; BASIC-NEXT: call void @func_argattr(i32* [[P2]], i32* [[P3]])
29+
; BASIC-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(i32* [[P]]), "nonnull"(i32* [[P1]]) ]
30+
; BASIC-NEXT: call void @func(i32* nonnull [[P1]], i32* nonnull [[P]])
31+
; BASIC-NEXT: ret void
32+
;
33+
; ALL-LABEL: @test(
34+
; ALL-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P:%.*]], i64 16), "nonnull"(i32* [[P]]) ]
35+
; ALL-NEXT: call void @func(i32* nonnull dereferenceable(16) [[P]], i32* null)
36+
; ALL-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P1:%.*]], i64 12), "nonnull"(i32* [[P]]) ]
37+
; ALL-NEXT: call void @func(i32* dereferenceable(12) [[P1]], i32* nonnull [[P]])
38+
; ALL-NEXT: call void @llvm.assume(i1 true) [ "cold"(), "dereferenceable"(i32* [[P1]], i64 12) ]
39+
; ALL-NEXT: call void @func_cold(i32* dereferenceable(12) [[P1]]) #0
40+
; ALL-NEXT: call void @llvm.assume(i1 true) [ "cold"(), "dereferenceable"(i32* [[P1]], i64 12) ]
41+
; ALL-NEXT: call void @func_cold(i32* dereferenceable(12) [[P1]])
42+
; ALL-NEXT: call void @func(i32* [[P1]], i32* [[P]])
43+
; ALL-NEXT: call void @llvm.assume(i1 true) [ "no-jump-tables"() ]
44+
; ALL-NEXT: call void @func_strbool(i32* [[P1]])
45+
; ALL-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P]], i64 8), "dereferenceable"(i32* [[P]], i64 16) ]
46+
; ALL-NEXT: call void @func(i32* dereferenceable(16) [[P]], i32* dereferenceable(8) [[P]])
47+
; ALL-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P1]], i64 8), "less-precise-fpmad"(), "no-jump-tables"(), "norecurse"(), "nounwind"(), "willreturn"() ]
48+
; ALL-NEXT: call void @func_many(i32* align 8 [[P1]])
49+
; ALL-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P2:%.*]], i64 8), "nonnull"(i32* [[P3:%.*]]), "nounwind"() ]
50+
; ALL-NEXT: call void @func_argattr(i32* [[P2]], i32* [[P3]])
51+
; ALL-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(i32* [[P]]), "nonnull"(i32* [[P1]]) ]
52+
; ALL-NEXT: call void @func(i32* nonnull [[P1]], i32* nonnull [[P]])
53+
; ALL-NEXT: ret void
54+
;
55+
call void @func(i32* nonnull dereferenceable(16) %P, i32* null)
56+
call void @func(i32* dereferenceable(12) %P1, i32* nonnull %P)
57+
call void @func_cold(i32* dereferenceable(12) %P1) cold
58+
call void @func_cold(i32* dereferenceable(12) %P1)
59+
call void @func(i32* %P1, i32* %P)
60+
call void @func_strbool(i32* %P1)
61+
call void @func(i32* dereferenceable(16) %P, i32* dereferenceable(8) %P)
62+
call void @func_many(i32* align 8 %P1)
63+
call void @func_argattr(i32* %P2, i32* %P3)
64+
call void @func(i32* nonnull %P1, i32* nonnull %P)
65+
ret void
66+
}

0 commit comments

Comments
 (0)