Skip to content

Commit c69a311

Browse files
authored
[Driver][FPGA][SYCL] Add specific timing diagnostic for FPGA AOT (#3965)
There are cases in which the generation of FPGA device code from 'aoc' is known to have timing issues. Add a specific diagnostic from the driver when this case is encountered. A special return code (42) is used to inform the driver of this situation. Additionally in this situation, we also want to allow for the compilation to continue so the user can use the generated binary knowing the timing issue as stated.
1 parent d8a1f33 commit c69a311

File tree

8 files changed

+118
-6
lines changed

8 files changed

+118
-6
lines changed

clang/include/clang/Driver/Job.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ struct ResponseFileSupport {
106106
class Command {
107107
public:
108108
using ErrorCodeDiagMapTy = llvm::DenseMap<int, std::string>;
109+
using ErrorCodeExitMapTy = llvm::DenseMap<int, bool>;
109110

110111
private:
111112
/// Source - The action which caused the creation of this job.
@@ -132,6 +133,11 @@ class Command {
132133
/// "invalid input" error can be ruled out
133134
ErrorCodeDiagMapTy ErrorCodeDiagMap;
134135

136+
/// Similar to the container for the diagnostic messages, this container
137+
/// is used to signify if the toolchain should error and exit right away
138+
/// or if we should continue compilation.
139+
ErrorCodeExitMapTy ErrorCodeExitMap;
140+
135141
/// The list of program arguments (not including the implicit first
136142
/// argument, which will be the executable).
137143
llvm::opt::ArgStringList Arguments;
@@ -198,11 +204,19 @@ class Command {
198204
/// returned by the command
199205
void addDiagForErrorCode(int ErrorCode, StringRef CustomDiag);
200206

207+
/// Store if the compilation should exit upon a particular error code
208+
/// returned by the command
209+
void addExitForErrorCode(int ErrorCode, bool Exit);
210+
201211
/// Get the custom driver diagnostic message for a particular error code
202212
/// if such was stored. Returns an empty string if no diagnostic message
203213
/// was found for the given error code.
204214
StringRef getDiagForErrorCode(int ErrorCode) const;
205215

216+
/// Will the tool exit when a particular error code is encountered. Returns
217+
/// true if not set (always exit)
218+
bool getWillExitForErrorCode(int ErrorCode) const;
219+
206220
/// getSource - Return the Action which caused the creation of this job.
207221
const Action &getSource() const { return Source; }
208222

clang/lib/Driver/Compilation.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,10 @@ static bool ActionFailed(const Action *A,
249249
if (FailingCommands.empty())
250250
return false;
251251

252+
for (const auto &CI : FailingCommands)
253+
if (!CI.second->getWillExitForErrorCode(CI.first))
254+
return false;
255+
252256
// CUDA/HIP/SYCL can have the same input source code compiled multiple times
253257
// so do not compile again if there are already failures. It is OK to abort
254258
// the CUDA pipeline on errors.
@@ -285,7 +289,9 @@ void Compilation::ExecuteJobs(const JobList &Jobs,
285289
if (int Res = ExecuteCommand(Job, FailingCommand)) {
286290
FailingCommands.push_back(std::make_pair(Res, FailingCommand));
287291
// Bail as soon as one command fails in cl driver mode.
288-
if (TheDriver.IsCLMode())
292+
// Do not bail when the tool is setup to allow for continuation upon
293+
// failure.
294+
if (TheDriver.IsCLMode() && FailingCommand->getWillExitForErrorCode(Res))
289295
return;
290296
}
291297
}

clang/lib/Driver/Job.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,13 +162,24 @@ void Command::addDiagForErrorCode(int ErrorCode, StringRef CustomDiag) {
162162
ErrorCodeDiagMap[ErrorCode] = CustomDiag.str();
163163
}
164164

165+
void Command::addExitForErrorCode(int ErrorCode, bool Exit) {
166+
ErrorCodeExitMap[ErrorCode] = Exit;
167+
}
168+
165169
StringRef Command::getDiagForErrorCode(int ErrorCode) const {
166170
auto ErrorCodeDiagIt = ErrorCodeDiagMap.find(ErrorCode);
167171
if (ErrorCodeDiagIt != ErrorCodeDiagMap.end())
168172
return ErrorCodeDiagIt->second;
169173
return StringRef();
170174
}
171175

176+
bool Command::getWillExitForErrorCode(int ErrorCode) const {
177+
auto ErrorCodeExitIt = ErrorCodeExitMap.find(ErrorCode);
178+
if (ErrorCodeExitIt != ErrorCodeExitMap.end())
179+
return ErrorCodeExitIt->second;
180+
return true;
181+
}
182+
172183
/// Rewrite relative include-like flag paths to absolute ones.
173184
static void
174185
rewriteIncludes(const llvm::ArrayRef<const char *> &Args, size_t Idx,

clang/lib/Driver/ToolChains/SYCL.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,17 @@ const char *SYCL::Linker::constructLLVMSpirvCommand(
8585
return OutputFileName;
8686
}
8787

88+
static void addFPGATimingDiagnostic(std::unique_ptr<Command> &Cmd,
89+
Compilation &C) {
90+
const char *Msg = C.getArgs().MakeArgString(
91+
"The FPGA image generated during this compile contains timing violations "
92+
"and may produce functional errors if used. Refer to the Intel oneAPI "
93+
"DPC++ FPGA Optimization Guide section on Timing Failures for more "
94+
"information.");
95+
Cmd->addDiagForErrorCode(/*ErrorCode*/ 42, Msg);
96+
Cmd->addExitForErrorCode(/*ErrorCode*/ 42, false);
97+
}
98+
8899
void SYCL::constructLLVMForeachCommand(Compilation &C, const JobAction &JA,
89100
std::unique_ptr<Command> InputCommand,
90101
const InputInfoList &InputFiles,
@@ -122,8 +133,14 @@ void SYCL::constructLLVMForeachCommand(Compilation &C, const JobAction &JA,
122133
SmallString<128> ForeachPath(C.getDriver().Dir);
123134
llvm::sys::path::append(ForeachPath, "llvm-foreach");
124135
const char *Foreach = C.getArgs().MakeArgString(ForeachPath);
125-
C.addCommand(std::make_unique<Command>(JA, *T, ResponseFileSupport::None(),
126-
Foreach, ForeachArgs, None));
136+
137+
auto Cmd = std::make_unique<Command>(JA, *T, ResponseFileSupport::None(),
138+
Foreach, ForeachArgs, None);
139+
// FIXME: Add the FPGA specific timing diagnostic to the foreach call.
140+
// The foreach call obscures the return codes from the tool it is calling
141+
// to the compiler itself.
142+
addFPGATimingDiagnostic(Cmd, C);
143+
C.addCommand(std::move(Cmd));
127144
}
128145

129146
// The list should match pre-built SYCL device library files located in
@@ -530,6 +547,7 @@ void SYCL::fpga::BackendCompiler::ConstructJob(
530547
const char *Exec = C.getArgs().MakeArgString(ExecPath);
531548
auto Cmd = std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),
532549
Exec, CmdArgs, None);
550+
addFPGATimingDiagnostic(Cmd, C);
533551
if (!ForeachInputs.empty())
534552
constructLLVMForeachCommand(C, JA, std::move(Cmd), ForeachInputs, Output,
535553
this, ReportOptArg, ForeachExt);

clang/test/Driver/Inputs/SYCL/aoc

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/bin/sh
2+
## Simple aoc replacement script that returns '42' that is used to test
3+
## special return code processing for FPGA AOT.
4+
5+
while [[ $# -gt 0 ]] ; do
6+
opt="$1"
7+
case $opt in
8+
-o)
9+
shift
10+
OUTFILE=$1
11+
;;
12+
*)
13+
shift
14+
;;
15+
esac
16+
done
17+
18+
if [ "$OUTFILE" != "" ] ; then
19+
echo "dummy file contents" > $OUTFILE
20+
fi
21+
22+
exit 42
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// REQUIRES: system-linux
2+
3+
/// Tests behavior with aoc -fintelpga.
4+
/// Uses a dummy aoc which returns '42' to make sure we properly emit a
5+
/// diagnostic and also do not stop compilation
6+
// RUN: env PATH=%S/Inputs/SYCL:$PATH \
7+
// RUN: not %clangxx -fsycl -fintelfpga -Xshardware %s -v 2>&1 \
8+
// RUN: | FileCheck %s -check-prefix ERROR_OUTPUT
9+
// ERROR_OUTPUT: ld{{.*}} -o a.out
10+
// ERROR_OUTPUT: The FPGA image generated during this compile contains timing violations
11+
12+
int main()
13+
{
14+
return 0;
15+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// REQUIRES: system-linux
2+
// Test that the return value from the called tool is retained.
3+
// Runs a script within a script so we can retain the return code without
4+
// the testing infrastructure getting in the way.
5+
6+
// RUN: echo 'Content of first file' > %t1.tgt
7+
// RUN: echo 'Content of second file' > %t2.tgt
8+
// RUN: echo "%t1.tgt" > %t.list
9+
// RUN: echo "%t2.tgt" >> %t.list
10+
11+
// RUN: echo "#!/bin/sh" > %t.sh
12+
// RUN: echo "cat \$1" >> %t.sh
13+
// RUN: echo "exit 21" >> %t.sh
14+
// RUN: chmod 777 %t.sh
15+
// RUN: echo "#!/bin/sh" > %t2.sh
16+
// RUN: echo "llvm-foreach --in-replace=\"{}\" --in-file-list=%t.list -- %t.sh \"{}\" > %t.res" >> %t2.sh
17+
// RUN: echo "echo \$? >> %t.res" >> %t2.sh
18+
// RUN: chmod 777 %t2.sh
19+
// RUN: %t2.sh
20+
// RUN: FileCheck < %t.res %s
21+
// CHECK: Content of first file
22+
// CHECK: Content of second file
23+
// CHECK: 21

llvm/tools/llvm-foreach/llvm-foreach.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ int main(int argc, char **argv) {
165165
if (!OutputFileList.empty())
166166
error(EC, "error opening the file '" + OutputFileList + "'");
167167

168+
int Res = 0;
168169
std::string ResOutArg;
169170
std::string IncOutArg;
170171
std::vector<std::string> ResInArgs(InReplaceArgs.size());
@@ -225,13 +226,15 @@ int main(int argc, char **argv) {
225226
int Result =
226227
sys::ExecuteAndWait(Prog, Args, /*Env=*/None, /*Redirects=*/None,
227228
/*SecondsToWait=*/0, /*MemoryLimit=*/0, &ErrMsg);
228-
if (Result != 0)
229-
error(ErrMsg);
229+
if (Result != 0) {
230+
errs() << "llvm-foreach: " << ErrMsg << '\n';
231+
Res = Result;
232+
}
230233
}
231234

232235
if (!OutputFileList.empty()) {
233236
OS.close();
234237
}
235238

236-
return 0;
239+
return Res;
237240
}

0 commit comments

Comments
 (0)