Skip to content

Commit 355e4bf

Browse files
committed
[WIP] Build assume from call
Summary: 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. Reviewers: jdoerfert Reviewed By: jdoerfert Subscribers: mgrang, fhahn, mgorny, hiraditya, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D72475
1 parent 9b9e2da commit 355e4bf

File tree

9 files changed

+298
-10
lines changed

9 files changed

+298
-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 moving and deleting instruction as those
11+
// transformation may remove or worsen information that can be derived from te
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+
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 AttrKind::ENUM_NAME: \
193+
return #DISPLAY_NAME;
194+
#include "llvm/IR/Attributes.inc"
195+
case AttrKind::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: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
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+
template <> struct DenseMapInfo<AssumedKnowledge> {
40+
static AssumedKnowledge getEmptyKey() {
41+
return {nullptr, nullptr, {nullptr, AssumedKnowledge::Empty}};
42+
}
43+
static AssumedKnowledge getTombstoneKey() {
44+
return {nullptr, nullptr, {nullptr, AssumedKnowledge::Tombstone}};
45+
}
46+
static unsigned getHashValue(const AssumedKnowledge &AK) {
47+
return hash_combine(AK.Name, AK.Argument, AK.WasOn.getPointer());
48+
}
49+
static bool isEqual(const AssumedKnowledge &LHS,
50+
const AssumedKnowledge &RHS) {
51+
return LHS.WasOn == RHS.WasOn && LHS.Name == RHS.Name &&
52+
LHS.Argument == RHS.Argument;
53+
}
54+
};
55+
56+
namespace {
57+
58+
/// Deterministically compare OperandBundleDef.
59+
/// The ordering is:
60+
/// - by the name of the attribute, (doesn't change)
61+
/// - then by the Value of the argument, (doesn't change)
62+
/// - lastly by the Name of the current Value it WasOn. (may change)
63+
/// This order is deterministic and allows looking for the right kind of
64+
/// attribute with binary search. However finding the right WasOn needs to be
65+
/// done via linear search because values can get remplaced.
66+
bool isLowerOpBundle(const OperandBundleDef &LHS, const OperandBundleDef &RHS) {
67+
auto getTuple = [](const OperandBundleDef &Op) {
68+
return std::make_tuple(
69+
Op.getTag(),
70+
Op.input_size() < 2
71+
? 0
72+
: cast<ConstantInt>(*std::next(Op.input_begin()))->getZExtValue(),
73+
Op.input_size() < 1 ? StringRef("") : (*Op.input_begin())->getName());
74+
};
75+
return getTuple(LHS) < getTuple(RHS);
76+
}
77+
78+
/// This class contain all knowledge that have been gather while building an
79+
/// llvm.assume and the function to manipulate it.
80+
struct AssumeBuilderState {
81+
Module *M;
82+
83+
SmallDenseSet<AssumedKnowledge, 8> AssumedKnowledgeSet;
84+
85+
AssumeBuilderState(Module *M) : M(M) {}
86+
87+
void addAttribute(Attribute Attr, Value *WasOn) {
88+
StringRef Name;
89+
Value *AttrArg = nullptr;
90+
if (Attr.isStringAttribute())
91+
if (ShouldPreserveAllAttributes)
92+
Name = Attr.getKindAsString();
93+
else
94+
return;
95+
else
96+
Name = Attribute::getNameFromAttrKind(Attr.getKindAsEnum());
97+
if (Attr.isIntAttribute())
98+
AttrArg = ConstantInt::get(Type::getInt64Ty(M->getContext()),
99+
Attr.getValueAsInt());
100+
AssumedKnowledgeSet.insert(
101+
{Name.data(), AttrArg, {WasOn, AssumedKnowledge::None}});
102+
}
103+
104+
void addCall(const CallBase *Call) {
105+
auto addAttrList = [&](AttributeList AttrList) {
106+
for (unsigned Idx = AttributeList::FirstArgIndex;
107+
Idx < AttrList.getNumAttrSets(); Idx++)
108+
for (Attribute Attr : AttrList.getAttributes(Idx))
109+
addAttribute(Attr, Call->getArgOperand(Idx - 1));
110+
if (ShouldPreserveAllAttributes)
111+
for (Attribute Attr : AttrList.getFnAttributes())
112+
addAttribute(Attr, nullptr);
113+
};
114+
addAttrList(Call->getAttributes());
115+
if (Function *Fn = Call->getCalledFunction())
116+
addAttrList(Fn->getAttributes());
117+
}
118+
119+
CallInst *build() {
120+
if (AssumedKnowledgeSet.empty())
121+
return nullptr;
122+
Function *FnAssume = Intrinsic::getDeclaration(M, Intrinsic::assume);
123+
LLVMContext &C = M->getContext();
124+
SmallVector<OperandBundleDef, 8> OpBundle;
125+
for (const AssumedKnowledge &Elem : AssumedKnowledgeSet) {
126+
SmallVector<Value *, 2> Args;
127+
if (Elem.WasOn.getPointer())
128+
Args.push_back(Elem.WasOn.getPointer());
129+
if (Elem.Argument)
130+
Args.push_back(Elem.Argument);
131+
OpBundle.push_back(OperandBundleDefT<Value *>(Elem.Name, Args));
132+
}
133+
llvm::sort(OpBundle, isLowerOpBundle);
134+
return CallInst::Create(
135+
FnAssume, ArrayRef<Value *>({ConstantInt::getTrue(C)}), OpBundle);
136+
}
137+
138+
void addInstruction(const Instruction *I) {
139+
if (auto *Call = dyn_cast<CallBase>(I))
140+
addCall(Call);
141+
// TODO: Add support for the other Instructions.
142+
// TODO: Maybe we should look around and merge with other llvm.assume.
143+
}
144+
};
145+
146+
} // namespace
147+
148+
CallInst *llvm::BuildAssumeFromInst(const Instruction *I, Module *M) {
149+
AssumeBuilderState Builder(M);
150+
Builder.addInstruction(I);
151+
return Builder.build();
152+
}
153+
154+
PreservedAnalyses AssumeBuilderPass::run(Function &F,
155+
FunctionAnalysisManager &AM) {
156+
for (Instruction &I : instructions(F))
157+
if (Instruction *Assume = BuildAssumeFromInst(&I))
158+
Assume->insertBefore(&I);
159+
return PreservedAnalyses::all();
160+
}
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)