Skip to content

Commit c7313ef

Browse files
committed
[vecz] Provide more context when CFG conversion fails
This should help debugging (using `--pass-remarks-missed=vecz`) why control-flow conversion failed to apply masks to the CFG. The previous diagnostics would only print the name of the function that couldn't be converted, but not any more specific information. This commit adds an extra level of information via a 'note', which is optionally printed on the line after the main diagnostic. This is not intended to be a full solution to better vecz diagnostics, but a good first step.
1 parent 107bc22 commit c7313ef

File tree

4 files changed

+102
-24
lines changed

4 files changed

+102
-24
lines changed

llvm/lib/SYCLNativeCPUUtils/compiler_passes/vecz/source/debugging.cpp

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,31 +26,39 @@ namespace vecz {
2626
///
2727
/// @param[in] V The value (can be `nullptr`) to be included in the remark
2828
/// @param[in] Msg The main remark message
29+
/// @param[in] Note An optional additional note to provide more context/info.
2930
/// @return The remark message as it is to be printed
30-
static std::string createRemarkMessage(const Value *V, StringRef Msg) {
31+
static std::string createRemarkMessage(const Value *V, StringRef Msg,
32+
StringRef Note = "") {
3133
std::string helper_str("Vecz: ");
3234
raw_string_ostream helper_stream(helper_str);
3335
helper_stream << Msg;
3436
if (V) {
3537
if (isa<Instruction>(V)) {
3638
// Instructions are already prefixed by two spaces when printed
37-
V->print(helper_stream, true);
39+
V->print(helper_stream, /*IsForDebug=*/true);
3840
} else if (const Function *F = dyn_cast<Function>(V)) {
39-
// Printing a functions leads to it's whole body being printed
41+
// Printing a functions leads to its whole body being printed
4042
helper_stream << " function \"" << F->getName() << "\"";
4143
} else {
4244
helper_stream << " ";
43-
V->print(helper_stream, true);
45+
V->print(helper_stream, /*IsForDebug=*/true);
4446
}
4547
}
4648
helper_stream << '\n';
4749

50+
// Provide extra context, if supplied
51+
if (!Note.empty()) {
52+
helper_stream << " note: " << Note << '\n';
53+
}
54+
4855
return helper_stream.str();
4956
}
5057

51-
void emitVeczRemarkMissed(const Function *F, const Value *V, StringRef Msg) {
58+
void emitVeczRemarkMissed(const Function *F, const Value *V, StringRef Msg,
59+
StringRef Note) {
5260
const Instruction *I = V ? dyn_cast<Instruction>(V) : nullptr;
53-
auto RemarkMsg = createRemarkMessage(V, Msg);
61+
auto RemarkMsg = createRemarkMessage(V, Msg, Note);
5462
OptimizationRemarkEmitter ORE(F);
5563
if (I) {
5664
ORE.emit(OptimizationRemarkMissed("vecz", "vecz", I) << RemarkMsg);
@@ -61,8 +69,8 @@ void emitVeczRemarkMissed(const Function *F, const Value *V, StringRef Msg) {
6169
}
6270
}
6371

64-
void emitVeczRemarkMissed(const Function *F, StringRef Msg) {
65-
emitVeczRemarkMissed(F, nullptr, Msg);
72+
void emitVeczRemarkMissed(const Function *F, StringRef Msg, StringRef Note) {
73+
emitVeczRemarkMissed(F, nullptr, Msg, Note);
6674
}
6775

6876
void emitVeczRemark(const Function *F, const Value *V, StringRef Msg) {

llvm/lib/SYCLNativeCPUUtils/compiler_passes/vecz/source/include/debugging.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <llvm/IR/Module.h>
2929
#include <llvm/IR/PassManager.h>
3030
#include <llvm/IR/Value.h>
31+
#include <llvm/Support/Error.h>
3132
#include <llvm/Support/raw_ostream.h>
3233

3334
#include <cstdlib>
@@ -64,6 +65,12 @@ struct VeczFailResult {
6465
operator std::optional<T>() const {
6566
return std::nullopt;
6667
}
68+
69+
/// @brief For functions that return an llvm::Error
70+
operator llvm::Error() const {
71+
return llvm::make_error<llvm::StringError>("Unknown VeczFailResult",
72+
llvm::inconvertibleErrorCode());
73+
}
6774
};
6875

6976
struct AnalysisFailResult : public internal::VeczFailResult {
@@ -175,13 +182,16 @@ struct AnalysisFailResult : public internal::VeczFailResult {
175182
/// @param[in] F The function in which we are currently working
176183
/// @param[in] V The value (can be `nullptr`) to be included in the message
177184
/// @param[in] Msg The main remark message text
185+
/// @param[in] Note An optional additional note to provide more context/info.
178186
void emitVeczRemarkMissed(const llvm::Function *F, const llvm::Value *V,
179-
llvm::StringRef Msg);
187+
llvm::StringRef Msg, llvm::StringRef Note = "");
180188
/// @brief Emit a RemarkMissed message
181189
///
182190
/// @param[in] F The function in which we are currently working
183191
/// @param[in] Msg The main remark message text
184-
void emitVeczRemarkMissed(const llvm::Function *F, llvm::StringRef Msg);
192+
/// @param[in] Note An optional additional note to provide more context/info.
193+
void emitVeczRemarkMissed(const llvm::Function *F, llvm::StringRef Msg,
194+
llvm::StringRef Note = "");
185195
/// @brief Emit a Remark message
186196
///
187197
/// @param[in] F The function in which we are currently working

llvm/lib/SYCLNativeCPUUtils/compiler_passes/vecz/source/transform/control_flow_conversion_pass.cpp

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <llvm/IR/Dominators.h>
3131
#include <llvm/IR/IRBuilder.h>
3232
#include <llvm/Support/Debug.h>
33+
#include <llvm/Support/Error.h>
3334
#include <llvm/Support/raw_ostream.h>
3435

3536
#include <queue>
@@ -145,17 +146,19 @@ class ControlFlowConversionState::Impl : public ControlFlowConversionState {
145146
/// @brief Apply masks to basic blocks in the function, to prevent
146147
/// side-effects for inactive instances.
147148
///
148-
/// @return true if masks were applied successfully, false otherwise.
149-
bool applyMasks();
149+
/// @return llvm::Error::success if masks were applied successfully, an error
150+
/// message explaining the failure otherwise.
151+
Error applyMasks();
150152

151153
/// @brief Apply a mask to the given basic block, to prevent side-effects for
152154
/// inactive instances.
153155
///
154156
/// @param[in] BB Basic block to apply masks to.
155157
/// @param[in] mask Mask to apply.
156158
///
157-
/// @return true if masks were applied successfully, false otherwise.
158-
bool applyMask(BasicBlock &BB, Value *mask);
159+
/// @return llvm::Error::success if masks were applied successfully, an error
160+
/// message explaining the failure otherwise.
161+
Error applyMask(BasicBlock &BB, Value *mask);
159162

160163
/// @brief Emit a call instructions to the masked version of the called
161164
/// function.
@@ -378,6 +381,14 @@ Instruction *copyExitMask(Value *mask, StringRef base, BasicBlock &BB) {
378381
VECZ_ERROR_IF(!mask, "Trying to copy exit mask with invalid arguments");
379382
return copyMask(mask, base + ".exit_mask", BB.getTerminator());
380383
}
384+
385+
/// Wrap a string into an llvm::StringError, pointing to an instruction.
386+
static inline Error makeStringError(const Twine &message, Instruction &I) {
387+
std::string helper_str = message.str();
388+
raw_string_ostream helper_stream(helper_str);
389+
helper_stream << " " << I;
390+
return make_error<StringError>(helper_stream.str(), inconvertibleErrorCode());
391+
}
381392
} // namespace
382393

383394
////////////////////////////////////////////////////////////////////////////////
@@ -538,8 +549,9 @@ bool ControlFlowConversionState::Impl::convertToDataFlow() {
538549
"Could not generate masks for");
539550
return false;
540551
}
541-
if (!applyMasks()) {
542-
emitVeczRemarkMissed(&F, VU.scalarFunction(), "Could not apply masks for");
552+
if (auto err = applyMasks()) {
553+
emitVeczRemarkMissed(&F, VU.scalarFunction(), "Could not apply masks for",
554+
llvm::toString(std::move(err)));
543555
return false;
544556
}
545557

@@ -1075,19 +1087,21 @@ bool ControlFlowConversionState::Impl::createCombinedLoopExitMask(
10751087
return true;
10761088
}
10771089

1078-
bool ControlFlowConversionState::Impl::applyMasks() {
1090+
Error ControlFlowConversionState::Impl::applyMasks() {
10791091
for (auto &BB : F) {
10801092
// Use masks with instructions that have side-effects.
10811093
if (!DR->isUniform(BB) && !DR->isByAll(BB)) {
10821094
auto *const entryMask = MaskInfos[&BB].entryMask;
10831095
VECZ_ERROR_IF(!entryMask, "BasicBlock should have an entry mask");
1084-
VECZ_FAIL_IF(!applyMask(BB, entryMask));
1096+
if (auto err = applyMask(BB, entryMask)) {
1097+
return err;
1098+
}
10851099
}
10861100
}
1087-
return true;
1101+
return Error::success();
10881102
}
10891103

1090-
bool ControlFlowConversionState::Impl::applyMask(BasicBlock &BB, Value *mask) {
1104+
Error ControlFlowConversionState::Impl::applyMask(BasicBlock &BB, Value *mask) {
10911105
// Packetization hasn't happened yet so this better be a scalar 1 bit int.
10921106
assert(mask->getType()->isIntegerTy(1) && "CFG mask type should be int1");
10931107
// Map the unmasked instruction with the masked one.
@@ -1102,17 +1116,17 @@ bool ControlFlowConversionState::Impl::applyMask(BasicBlock &BB, Value *mask) {
11021116
// Turn loads and stores into masked loads and stores.
11031117
if (memOp && (memOp->isLoad() || memOp->isStore())) {
11041118
if (!tryApplyMaskToMemOp(*memOp, mask, toDelete)) {
1105-
return false;
1119+
return makeStringError("Could not apply mask to MemOp", I);
11061120
}
11071121
} else if (auto *CI = dyn_cast<CallInst>(&I)) {
11081122
// Turn calls into masked calls if possible.
11091123
if (!applyMaskToCall(CI, mask, toDelete)) {
1110-
return false;
1124+
return makeStringError("Could not apply mask to call instruction", I);
11111125
}
11121126
} else if (I.isAtomic() && !isa<FenceInst>(&I)) {
11131127
// We need to apply masks to atomic functions, but it is currently not
11141128
// implemented. See CA-3294.
1115-
return false;
1129+
return makeStringError("Could not apply mask to atomic instruction", I);
11161130
} else if (auto *branch = dyn_cast<BranchInst>(&I)) {
11171131
// We have to be careful with infinite loops, because if they exist on a
11181132
// divergent code path, they will always be entered and will hang the
@@ -1138,7 +1152,8 @@ bool ControlFlowConversionState::Impl::applyMask(BasicBlock &BB, Value *mask) {
11381152
updateMaps(unmasked, masked);
11391153
IRCleanup::deleteInstructionNow(unmasked);
11401154
}
1141-
return true;
1155+
1156+
return Error::success();
11421157
}
11431158

11441159
CallInst *ControlFlowConversionState::Impl::emitMaskedVersion(CallInst *CI,
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
; Copyright (C) Codeplay Software Limited
2+
;
3+
; Licensed under the Apache License, Version 2.0 (the "License") with LLVM
4+
; Exceptions; you may not use this file except in compliance with the License.
5+
; You may obtain a copy of the License at
6+
;
7+
; https://github.com/codeplaysoftware/oneapi-construction-kit/blob/main/LICENSE.txt
8+
;
9+
; Unless required by applicable law or agreed to in writing, software
10+
; distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
; WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
; License for the specific language governing permissions and limitations
13+
; under the License.
14+
;
15+
; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
16+
17+
; RUN: veczc -w 4 -vecz-passes=cfg-convert,verify -S \
18+
; RUN: --pass-remarks-missed=vecz < %s 2>&1 | FileCheck %s
19+
20+
target triple = "spir64-unknown-unknown"
21+
target datalayout = "e-p:64:64:64-m:e-i64:64-f80:128-n8:16:32:64-S128"
22+
23+
; CHECK: Vecz: Could not apply masks for function "kernel"
24+
; CHECK-NEXT: note: Could not apply mask to atomic instruction
25+
; CHECK-SAME: %atomic = atomicrmw add ptr %arrayidx.in, i32 2 monotonic, align 4
26+
27+
define spir_kernel void @kernel(ptr %in, ptr %out) {
28+
entry:
29+
%gid = tail call i64 @__mux_get_global_id(i32 0)
30+
%cmp = icmp eq i64 %gid, 0
31+
br i1 %cmp, label %if.then, label %end
32+
33+
if.then:
34+
%arrayidx.in = getelementptr inbounds i32, ptr %in, i64 %gid
35+
%atomic = atomicrmw add ptr %arrayidx.in, i32 2 monotonic, align 4
36+
br label %end
37+
38+
end:
39+
%merge = phi i32 [ 0, %entry ], [ %atomic, %if.then ]
40+
%arrayidx.out = getelementptr inbounds i32, ptr %out, i64 %gid
41+
store i32 %merge, ptr %arrayidx.out, align 4
42+
ret void
43+
}
44+
45+
declare i64 @__mux_get_global_id(i32)

0 commit comments

Comments
 (0)