|
40 | 40 | #include "llvm/Option/ArgList.h"
|
41 | 41 | #include "llvm/Support/Casting.h"
|
42 | 42 | #include "llvm/Support/CodeGen.h"
|
| 43 | +#include "llvm/Support/CommandLine.h" |
43 | 44 | #include "llvm/Support/Compiler.h"
|
44 | 45 | #include "llvm/Support/Compression.h"
|
45 | 46 | #include "llvm/Support/FileSystem.h"
|
@@ -4195,6 +4196,167 @@ static bool ContainsWrapperAction(const Action *A) {
|
4195 | 4196 | return false;
|
4196 | 4197 | }
|
4197 | 4198 |
|
| 4199 | +// Put together an external compiler compilation call which is used instead |
| 4200 | +// of the clang invocation for the host compile of an offload compilation. |
| 4201 | +// Enabling command line: clang++ -fsycl -fsycl-host-compiler=<HostExe> |
| 4202 | +// <ClangOpts> -fsycl-host-compiler-options=<HostOpts> |
| 4203 | +// Any <ClangOpts> used which are phase limiting (preprocessing, assembly, |
| 4204 | +// object generation) are specifically handled here by specifying the |
| 4205 | +// equivalent phase limiting option(s). |
| 4206 | +// It is expected that any user <HostOpts> options passed will be placed |
| 4207 | +// after any implied options set here. This will have overriding behaviors |
| 4208 | +// for any options which are considered to be evaluated from left to right. |
| 4209 | +// Specifying any <HostOpts> option which conficts any of the implied options |
| 4210 | +// will result in undefined behavior. Potential conflicting options: |
| 4211 | +// * Output specification options (-o, -Fo, -Fa, etc) |
| 4212 | +// * Phase limiting options (-E, -c, -P, etc) |
| 4213 | +void Clang::ConstructHostCompilerJob(Compilation &C, const JobAction &JA, |
| 4214 | + const InputInfo &Output, |
| 4215 | + const InputInfoList &Inputs, |
| 4216 | + const llvm::opt::ArgList &TCArgs) const { |
| 4217 | + |
| 4218 | + // The Host compilation step that occurs here is constructed based on the |
| 4219 | + // input from the user. This consists of the compiler to call and the |
| 4220 | + // options that will be used during the compilation. |
| 4221 | + ArgStringList HostCompileArgs; |
| 4222 | + const InputInfo &InputFile = Inputs.front(); |
| 4223 | + const ToolChain &TC = getToolChain(); |
| 4224 | + |
| 4225 | + // Input file. |
| 4226 | + HostCompileArgs.push_back(InputFile.getFilename()); |
| 4227 | + |
| 4228 | + // When performing the host compilation, we are expecting to only be |
| 4229 | + // creating intermediate files, namely preprocessor output, assembly or |
| 4230 | + // object files. |
| 4231 | + // We are making assumptions in regards to what options are used to |
| 4232 | + // generate these intermediate files. |
| 4233 | + // gcc/g++/clang/clang++/default | cl |
| 4234 | + // Object: -c | -c |
| 4235 | + // Preprocessed: -E | -P -Fi<file> |
| 4236 | + // Assembly: -S | -c -Fa<file> |
| 4237 | + // Header Input: -include <file> | -FI <file> |
| 4238 | + // |
| 4239 | + // The options used are determined by the compiler name and target triple. |
| 4240 | + Arg *HostCompilerDefArg = |
| 4241 | + TCArgs.getLastArg(options::OPT_fsycl_host_compiler_EQ); |
| 4242 | + assert(HostCompilerDefArg && "Expected host compiler designation."); |
| 4243 | + |
| 4244 | + bool OutputAdded = false; |
| 4245 | + StringRef CompilerName = |
| 4246 | + llvm::sys::path::stem(HostCompilerDefArg->getValue()); |
| 4247 | + if (CompilerName.empty()) |
| 4248 | + TC.getDriver().Diag(diag::err_drv_missing_arg_mtp) |
| 4249 | + << HostCompilerDefArg->getAsString(TCArgs); |
| 4250 | + // FIXME: Consider requiring user input to specify a compatibility class |
| 4251 | + // to determine the type of host compiler being used. |
| 4252 | + SmallVector<StringRef, 4> MSVCCompilers = {"cl", "clang-cl", "icl"}; |
| 4253 | + bool IsMSVCHostCompiler = |
| 4254 | + std::find(MSVCCompilers.begin(), MSVCCompilers.end(), CompilerName) != |
| 4255 | + MSVCCompilers.end(); |
| 4256 | + |
| 4257 | + auto addMSVCOutputFile = [&](StringRef Opt) { |
| 4258 | + SmallString<128> OutOpt(Opt); |
| 4259 | + OutOpt += Output.getFilename(); |
| 4260 | + HostCompileArgs.push_back(TCArgs.MakeArgString(OutOpt)); |
| 4261 | + OutputAdded = true; |
| 4262 | + }; |
| 4263 | + // FIXME: Reuse existing toolchains which are already supported to put |
| 4264 | + // together the options. |
| 4265 | + // FIXME: For any potential obscure host compilers that do not use the |
| 4266 | + // 'standard' set of options, we should provide a user interface that allows |
| 4267 | + // users to override the implied options. |
| 4268 | + if (isa<PreprocessJobAction>(JA)) { |
| 4269 | + if (IsMSVCHostCompiler) { |
| 4270 | + // Check the output file, if it is 'stdout' we want to use -E. |
| 4271 | + if (StringRef(Output.getFilename()).equals("-")) { |
| 4272 | + HostCompileArgs.push_back("-E"); |
| 4273 | + OutputAdded = true; |
| 4274 | + } else { |
| 4275 | + HostCompileArgs.push_back("-P"); |
| 4276 | + addMSVCOutputFile("-Fi"); |
| 4277 | + } |
| 4278 | + } else |
| 4279 | + HostCompileArgs.push_back("-E"); |
| 4280 | + } else if (isa<AssembleJobAction>(JA)) { |
| 4281 | + HostCompileArgs.push_back("-c"); |
| 4282 | + if (IsMSVCHostCompiler) |
| 4283 | + addMSVCOutputFile("-Fo"); |
| 4284 | + } else { |
| 4285 | + assert((isa<CompileJobAction, BackendJobAction>(JA)) && |
| 4286 | + "Invalid action for external host compilation tool."); |
| 4287 | + if (JA.getType() == types::TY_PP_Asm) { |
| 4288 | + if (IsMSVCHostCompiler) { |
| 4289 | + HostCompileArgs.push_back("-c"); |
| 4290 | + addMSVCOutputFile("-Fa"); |
| 4291 | + // The MSVC Compiler does not have a way to just create the assembly |
| 4292 | + // file so we create the assembly file and object file, and redirect |
| 4293 | + // the object file to a temporary. |
| 4294 | + std::string ObjTmpName = C.getDriver().GetTemporaryPath("host", "obj"); |
| 4295 | + StringRef WrapperFileName = |
| 4296 | + C.addTempFile(C.getArgs().MakeArgString(ObjTmpName)); |
| 4297 | + SmallString<128> ObjOutOpt("-Fo"); |
| 4298 | + ObjOutOpt += WrapperFileName; |
| 4299 | + HostCompileArgs.push_back(C.getArgs().MakeArgString(ObjOutOpt)); |
| 4300 | + } else |
| 4301 | + HostCompileArgs.push_back("-S"); |
| 4302 | + } else { |
| 4303 | + TC.getDriver().Diag(diag::err_drv_output_type_with_host_compiler); |
| 4304 | + } |
| 4305 | + } |
| 4306 | + |
| 4307 | + // Add default header search directories. |
| 4308 | + SmallString<128> BaseDir(C.getDriver().Dir); |
| 4309 | + llvm::sys::path::append(BaseDir, "..", "include"); |
| 4310 | + SmallString<128> SYCLDir(BaseDir); |
| 4311 | + llvm::sys::path::append(SYCLDir, "sycl"); |
| 4312 | + HostCompileArgs.push_back("-I"); |
| 4313 | + HostCompileArgs.push_back(TCArgs.MakeArgString(SYCLDir)); |
| 4314 | + HostCompileArgs.push_back("-I"); |
| 4315 | + HostCompileArgs.push_back(TCArgs.MakeArgString(BaseDir)); |
| 4316 | + |
| 4317 | + if (!OutputAdded) { |
| 4318 | + // Add output file to the command line. This is assumed to be prefaced |
| 4319 | + // with the '-o' option that is used to designate the output file. |
| 4320 | + HostCompileArgs.push_back("-o"); |
| 4321 | + HostCompileArgs.push_back(Output.getFilename()); |
| 4322 | + } |
| 4323 | + |
| 4324 | + // Add the integration header. |
| 4325 | + StringRef Header = |
| 4326 | + TC.getDriver().getIntegrationHeader(InputFile.getBaseInput()); |
| 4327 | + if (types::getPreprocessedType(InputFile.getType()) != types::TY_INVALID && |
| 4328 | + !Header.empty()) { |
| 4329 | + HostCompileArgs.push_back(IsMSVCHostCompiler ? "-FI" : "-include"); |
| 4330 | + HostCompileArgs.push_back(TCArgs.MakeArgString(Header)); |
| 4331 | + } |
| 4332 | + |
| 4333 | + SmallString<128> ExecPath; |
| 4334 | + if (HostCompilerDefArg) { |
| 4335 | + ExecPath = HostCompilerDefArg->getValue(); |
| 4336 | + if (!ExecPath.empty() && ExecPath == llvm::sys::path::stem(ExecPath)) |
| 4337 | + ExecPath = TC.GetProgramPath(ExecPath.c_str()); |
| 4338 | + } |
| 4339 | + |
| 4340 | + // Add any user-specified arguments. |
| 4341 | + if (Arg *HostCompilerOptsArg = |
| 4342 | + TCArgs.getLastArg(options::OPT_fsycl_host_compiler_options_EQ)) { |
| 4343 | + SmallVector<const char *, 8> TargetArgs; |
| 4344 | + llvm::BumpPtrAllocator BPA; |
| 4345 | + llvm::StringSaver S(BPA); |
| 4346 | + // Tokenize the string. |
| 4347 | + llvm::cl::TokenizeGNUCommandLine(HostCompilerOptsArg->getValue(), S, |
| 4348 | + TargetArgs); |
| 4349 | + llvm::transform(TargetArgs, std::back_inserter(HostCompileArgs), |
| 4350 | + [&TCArgs](StringRef A) { return TCArgs.MakeArgString(A); }); |
| 4351 | + } |
| 4352 | + const Tool *T = TC.SelectTool(JA); |
| 4353 | + auto Cmd = std::make_unique<Command>(JA, *T, ResponseFileSupport::None(), |
| 4354 | + TCArgs.MakeArgString(ExecPath), |
| 4355 | + HostCompileArgs, None); |
| 4356 | + |
| 4357 | + C.addCommand(std::move(Cmd)); |
| 4358 | +} |
| 4359 | + |
4198 | 4360 | void Clang::ConstructJob(Compilation &C, const JobAction &JA,
|
4199 | 4361 | const InputInfo &Output, const InputInfoList &Inputs,
|
4200 | 4362 | const ArgList &Args, const char *LinkingOutput) const {
|
@@ -4228,6 +4390,14 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
|
4228 | 4390 | IsHeaderModulePrecompile || Inputs.size() == 1) &&
|
4229 | 4391 | "Unable to handle multiple inputs.");
|
4230 | 4392 |
|
| 4393 | + // Perform the SYCL host compilation using an external compiler if the user |
| 4394 | + // requested. |
| 4395 | + if (Args.hasArg(options::OPT_fsycl_host_compiler_EQ) && IsSYCL && |
| 4396 | + !IsSYCLOffloadDevice) { |
| 4397 | + ConstructHostCompilerJob(C, JA, Output, Inputs, Args); |
| 4398 | + return; |
| 4399 | + } |
| 4400 | + |
4231 | 4401 | // A header module compilation doesn't have a main input file, so invent a
|
4232 | 4402 | // fake one as a placeholder.
|
4233 | 4403 | const char *ModuleName = [&]{
|
|
0 commit comments