LLVM: include/llvm/IR/GenericConvergenceVerifierImpl.h Source File (original) (raw)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26#ifndef LLVM_IR_GENERICCONVERGENCEVERIFIERIMPL_H

27#define LLVM_IR_GENERICCONVERGENCEVERIFIERIMPL_H

28

33

34#define Check(C, ...) \

35 do { \

36 if (!(C)) { \

37 reportFailure(__VA_ARGS__); \

38 return; \

39 } \

40 } while (false)

41

42#define CheckOrNull(C, ...) \

43 do { \

44 if (!(C)) { \

45 reportFailure(__VA_ARGS__); \

46 return {}; \

47 } \

48 } while (false)

49

50namespace llvm {

52 Tokens.clear();

53 CI.clear();

55}

56

57template

59 SeenFirstConvOp = false;

60}

61

62template

64 ConvOpKind ConvOp = getConvOp(I);

65

66 auto *TokenDef = findAndCheckConvergenceTokenUsed(I);

67 switch (ConvOp) {

68 case CONV_ENTRY:

69 Check(isInsideConvergentFunction(I),

70 "Entry intrinsic can occur only in a convergent function.",

71 {Context.print(&I)});

72 Check(I.getParent()->isEntryBlock(),

73 "Entry intrinsic can occur only in the entry block.",

74 {Context.print(&I)});

75 Check(!SeenFirstConvOp,

76 "Entry intrinsic cannot be preceded by a convergent operation in the "

77 "same basic block.",

78 {Context.print(&I)});

79 [[fallthrough]];

80 case CONV_ANCHOR:

82 "Entry or anchor intrinsic cannot have a convergencectrl token "

83 "operand.",

84 {Context.print(&I)});

85 break;

86 case CONV_LOOP:

87 Check(TokenDef, "Loop intrinsic must have a convergencectrl token operand.",

88 {Context.print(&I)});

89 Check(!SeenFirstConvOp,

90 "Loop intrinsic cannot be preceded by a convergent operation in the "

91 "same basic block.",

92 {Context.print(&I)});

93 break;

94 default:

95 break;

96 }

97

98 if (ConvOp != CONV_NONE)

99 checkConvergenceTokenProduced(I);

100

101 if (isConvergent(I))

102 SeenFirstConvOp = true;

103

104 if (TokenDef || ConvOp != CONV_NONE) {

105 Check(isConvergent(I),

106 "Convergence control token can only be used in a convergent call.",

107 {Context.print(&I)});

109 "Cannot mix controlled and uncontrolled convergence in the same "

110 "function.",

111 {Context.print(&I)});

113 } else if (isConvergent(I)) {

115 "Cannot mix controlled and uncontrolled convergence in the same "

116 "function.",

117 {Context.print(&I)});

119 }

120}

121

122template

125 FailureCB(Message);

126 if (OS) {

127 for (auto V : DumpedValues)

128 *OS << V << '\n';

129 }

130}

131

132template

134 assert(Context.getFunction());

135 const auto &F = *Context.getFunction();

136

139

140

141

142

143 CI.compute(const_cast<FunctionT &>(F));

144

148 "Convergence control token must dominate all its uses.",

149 {Context.print(Token), Context.print(User)});

150

152 "Convergence region is not well-nested.",

153 {Context.print(Token), Context.print(User)});

154 while (LiveTokens.back() != Token)

155 LiveTokens.pop_back();

156

157

158 auto *BB = User->getParent();

159 auto *BBCycle = CI.getCycle(BB);

160 if (!BBCycle)

161 return;

162

163 auto *DefBB = Token->getParent();

164 if (DefBB == BB || BBCycle->contains(DefBB)) {

165

166 return;

167 }

168

169 Check(getConvOp(*User) == CONV_LOOP,

170 "Convergence token used by an instruction other than "

171 "llvm.experimental.convergence.loop in a cycle that does "

172 "not contain the token's definition.",

173 {Context.print(User), CI.print(BBCycle)});

174

175 while (true) {

176 auto *Parent = BBCycle->getParentCycle();

177 if (!Parent || Parent->contains(DefBB))

178 break;

179 BBCycle = Parent;

180 };

181

182 Check(BBCycle->isReducible() && BB == BBCycle->getHeader(),

183 "Cycle heart must dominate all blocks in the cycle.",

184 {Context.print(User), Context.printAsOperand(BB), CI.print(BBCycle)});

186 "Two static convergence token uses in a cycle that does "

187 "not contain either token's definition.",

188 {Context.print(User), Context.print(CycleHearts[BBCycle]),

189 CI.print(BBCycle)});

190 CycleHearts[BBCycle] = User;

191 };

192

195 for (auto *BB : RPOT) {

196 LiveTokens.clear();

197 auto LTIt = LiveTokenMap.find(BB);

198 if (LTIt != LiveTokenMap.end()) {

199 LiveTokens = std::move(LTIt->second);

200 LiveTokenMap.erase(LTIt);

201 }

202

203 for (auto &I : *BB) {

204 if (auto *Token = Tokens.lookup(&I))

205 checkToken(Token, &I, LiveTokens);

206 if (getConvOp(I) != CONV_NONE)

208 }

209

210

212 auto *SuccNode = DT.getNode(Succ);

213 auto [LTIt, Inserted] = LiveTokenMap.try_emplace(Succ);

214 if (Inserted) {

215

216

217 for (auto LiveToken : LiveTokens) {

218 if (!DT.dominates(DT.getNode(LiveToken->getParent()), SuccNode))

219 break;

220 LTIt->second.push_back(LiveToken);

221 }

222 } else {

223

225 LTIt->second, [&LiveTokens](const InstructionT *Token) {

226 return llvm::is_contained(LiveTokens, Token);

227 });

228 LTIt->second.erase(It, LTIt->second.end());

229 }

230 }

231 }

232}

233

234}

235

236#endif

A verifier for the static rules of convergence control tokens that works with both LLVM IR and MIR.

This file builds on the ADT/GraphTraits.h file to build a generic graph post order iterator.

assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())

ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...

iterator find(const_arg_type_t< KeyT > Val)

std::pair< iterator, bool > try_emplace(KeyT &&Key, Ts &&...Args)

bool erase(const KeyT &Val)

size_type count(const_arg_type_t< KeyT > Val) const

Return 1 if the specified key is in the map, 0 otherwise.

DomTreeNodeBase< NodeT > * getNode(const NodeT *BB) const

getNode - return the (Post)DominatorTree node for the specified basic block.

bool dominates(const BasicBlock *BB, const Use &U) const

Return true if the (end of the) basic block BB dominates the use U.

typename ContextT::InstructionT InstructionT

typename ContextT::DominatorTreeT DominatorTreeT

typename ContextT::BlockT BlockT

typename ContextT::FunctionT FunctionT

void verify(const DominatorTreeT &DT)

void visit(const BlockT &BB)

This class consists of common code factored out of the SmallVector class to reduce code duplication b...

void push_back(const T &Elt)

This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.

Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...

This is an optimization pass for GlobalISel generic memory operations.

auto successors(const MachineBasicBlock *BB)

auto partition(R &&Range, UnaryPredicate P)

Provide wrappers to std::partition which take ranges instead of having to pass begin/end explicitly.

bool is_contained(R &&Range, const E &Element)

Returns true if Element is found in Range.