@@ -698,18 +698,49 @@ void Driver::CreateOffloadingDeviceToolChains(Compilation &C,
698
698
// SYCL
699
699
//
700
700
// We need to generate a SYCL toolchain if the user specified targets with
701
- // the -fsycl-targets option. If -fsycl is supplied without -fsycl-targets
702
- // we will assume SPIR-V
701
+ // the -fsycl-targets, -fsycl-add-targets or -fsycl-link- targets option.
702
+ // If -fsycl is supplied without any of these we will assume SPIR-V
703
703
bool HasValidSYCLRuntime = C.getInputArgs ().hasFlag (options::OPT_fsycl,
704
704
options::OPT_fno_sycl, false );
705
- if (Arg *SYCLTargets =
706
- C.getInputArgs ().getLastArg (options::OPT_fsycl_targets_EQ)) {
707
- if (SYCLTargets->getNumValues ()) {
705
+
706
+ Arg *SYCLTargets =
707
+ C.getInputArgs ().getLastArg (options::OPT_fsycl_targets_EQ);
708
+ Arg *SYCLLinkTargets =
709
+ C.getInputArgs ().getLastArg (options::OPT_fsycl_link_targets_EQ);
710
+ Arg *SYCLAddTargets =
711
+ C.getInputArgs ().getLastArg (options::OPT_fsycl_add_targets_EQ);
712
+ // -fsycl-targets cannot be used with -fsycl-link-targets
713
+ if (SYCLTargets && SYCLLinkTargets)
714
+ Diag (clang::diag::err_drv_sycl_target_conflict);
715
+ // -fsycl-link-targets and -fsycl-add-targets cannot be used together
716
+ if (SYCLLinkTargets && SYCLAddTargets)
717
+ Diag (clang::diag::err_drv_sycl_add_link_conflict);
718
+
719
+ // -fsycl-add-targets is a list of paired items (Triple and file) which are
720
+ // gathered and used to be linked into the final device binary. This can
721
+ // be used with -fsycl-targets to put together the final conglomerate binary
722
+ if (SYCLAddTargets) {
723
+ if (SYCLAddTargets->getNumValues ()) {
724
+ // -fsycl-add-targets should be used with -fsycl
725
+ if (HasValidSYCLRuntime) {
726
+ // Use of -fsycl-add-targets adds additional files to the SYCL device
727
+ // link step. Regular offload processing occurs below
728
+ } else
729
+ Diag (clang::diag::err_drv_expecting_fsycl_with_fsycl_targets)
730
+ << " -add-" ;
731
+ } else
732
+ Diag (clang::diag::warn_drv_empty_joined_argument)
733
+ << SYCLAddTargets->getAsString (C.getInputArgs ());
734
+ }
735
+ if (SYCLTargets || SYCLLinkTargets) {
736
+ // At this point, we know we have a valid -fsycl*target option passed
737
+ Arg * SYCLTargetsValues = SYCLTargets ? SYCLTargets : SYCLLinkTargets;
738
+ if (SYCLTargetsValues->getNumValues ()) {
708
739
// We expect that -fsycl-targets is always used in conjunction with the
709
740
// -fsycl option
710
741
if (HasValidSYCLRuntime) {
711
742
llvm::StringMap<const char *> FoundNormalizedTriples;
712
- for (const char *Val : SYCLTargets ->getValues ()) {
743
+ for (const char *Val : SYCLTargetsValues ->getValues ()) {
713
744
llvm::Triple TT (Val);
714
745
std::string NormalizedName = TT.normalize ();
715
746
@@ -742,11 +773,14 @@ void Driver::CreateOffloadingDeviceToolChains(Compilation &C,
742
773
C.addOffloadDeviceToolChain (SYCLTC.get (), Action::OFK_SYCL);
743
774
}
744
775
}
745
- } else
746
- Diag (clang::diag::err_drv_expecting_fsycl_with_fsycl_targets);
776
+ } else {
777
+ const char *syclArg = SYCLTargets ? " -" : " -link-" ;
778
+ Diag (clang::diag::err_drv_expecting_fsycl_with_fsycl_targets)
779
+ << syclArg;
780
+ }
747
781
} else
748
782
Diag (clang::diag::warn_drv_empty_joined_argument)
749
- << SYCLTargets ->getAsString (C.getInputArgs ());
783
+ << SYCLTargetsValues ->getAsString (C.getInputArgs ());
750
784
} else {
751
785
// If -fsycl is supplied without -fsycl-targets we will assume SPIR-V
752
786
if (HasValidSYCLRuntime) {
@@ -2933,9 +2967,18 @@ class OffloadingActionBuilder final {
2933
2967
// / and all the device linked images are passed to the host link phase.
2934
2968
// / SPIR related are wrapped before added to the fat binary
2935
2969
class SYCLActionBuilder final : public DeviceActionBuilder {
2970
+ // / Flag to signal if the user requested device-only compilation.
2971
+ bool CompileDeviceOnly = false ;
2972
+
2936
2973
// / The SYCL actions for the current input.
2937
2974
ActionList SYCLDeviceActions;
2938
2975
2976
+ // / The SYCL link binary if it was generated for the current input.
2977
+ Action *SYCLLinkBinary = nullptr ;
2978
+
2979
+ // / SYCL ahead of time compilation inputs
2980
+ SmallVector<std::pair<llvm::Triple, const char *>, 8 > SYCLAOTInputs;
2981
+
2939
2982
// / The linker inputs obtained for each toolchain.
2940
2983
SmallVector<ActionList, 8 > DeviceLinkerInputs;
2941
2984
@@ -2952,9 +2995,44 @@ class OffloadingActionBuilder final {
2952
2995
phases::ID CurPhase, phases::ID FinalPhase,
2953
2996
PhasesTy &Phases) override {
2954
2997
2955
- // We should always have an action for each input.
2956
- assert (SYCLDeviceActions.size () == ToolChains.size () &&
2957
- " Number of SYCL actions and toolchains do not match." );
2998
+ // With -fsycl-link-targets, we will take the unbundled binaries
2999
+ // for each device and link them together to a single binary that will
3000
+ // be used in a split compilation step.
3001
+ if (CompileDeviceOnly) {
3002
+ ActionList DeviceActions;
3003
+ for (auto Ph : Phases) {
3004
+ // Skip the phases that were already dealt with.
3005
+ if (Ph < CurPhase)
3006
+ continue ;
3007
+ // We have to be consistent with the host final phase.
3008
+ if (Ph > FinalPhase || Ph == phases::Link)
3009
+ break ;
3010
+ for (Action *&A : SYCLDeviceActions) {
3011
+ A = C.getDriver ().ConstructPhaseAction (C, Args, Ph, A,
3012
+ Action::OFK_SYCL);
3013
+ }
3014
+ }
3015
+ for (Action *&A : SYCLDeviceActions) {
3016
+ OffloadAction::DeviceDependences DDep;
3017
+ DDep.add (*A, *ToolChains.front (), /* BoundArch*/ nullptr ,
3018
+ Action::OFK_SYCL);
3019
+ DeviceActions.push_back (
3020
+ C.MakeAction <OffloadAction>(DDep, A->getType ()));
3021
+ }
3022
+
3023
+ // We generate the fat binary if we have device input actions.
3024
+ if (!DeviceActions.empty ()) {
3025
+ SYCLLinkBinary =
3026
+ C.MakeAction <LinkJobAction>(DeviceActions, types::TY_Image);
3027
+
3028
+ // Remove the SYCL actions as they are already connected to an host
3029
+ // action or fat binary.
3030
+ SYCLDeviceActions.clear ();
3031
+ }
3032
+
3033
+ // We avoid creating host action in device-only mode.
3034
+ return CompileDeviceOnly ? ABRT_Ignore_Host : ABRT_Success;
3035
+ }
2958
3036
2959
3037
// FIXME: This adds the integrated header generation pass before the
2960
3038
// Host compilation pass so the Host can use the header generated. This
@@ -3005,6 +3083,11 @@ class OffloadingActionBuilder final {
3005
3083
// If this is an input action replicate it for each SYCL toolchain.
3006
3084
if (auto *IA = dyn_cast<InputAction>(HostAction)) {
3007
3085
SYCLDeviceActions.clear ();
3086
+
3087
+ // libraries are not replicated for SYCL
3088
+ if (!types::isSrcFile (IA->getType ()))
3089
+ return ABRT_Inactive;
3090
+
3008
3091
for (unsigned I = 0 ; I < ToolChains.size (); ++I)
3009
3092
SYCLDeviceActions.push_back (
3010
3093
C.MakeAction <InputAction>(IA->getInputArg (), IA->getType ()));
@@ -3025,6 +3108,17 @@ class OffloadingActionBuilder final {
3025
3108
}
3026
3109
3027
3110
void appendTopLevelActions (ActionList &AL) override {
3111
+
3112
+ if (SYCLLinkBinary) {
3113
+ OffloadAction::DeviceDependences Dep;
3114
+ Dep.add (*SYCLLinkBinary, *ToolChains.front (), /* BoundArch=*/ nullptr ,
3115
+ Action::OFK_SYCL);
3116
+ AL.push_back (C.MakeAction <OffloadAction>(Dep, SYCLLinkBinary->getType ()));
3117
+ SYCLDeviceActions.clear ();
3118
+ SYCLLinkBinary = nullptr ;
3119
+ return ;
3120
+ }
3121
+
3028
3122
if (SYCLDeviceActions.empty ())
3029
3123
return ;
3030
3124
@@ -3048,19 +3142,46 @@ class OffloadingActionBuilder final {
3048
3142
assert (ToolChains.size () == DeviceLinkerInputs.size () &&
3049
3143
" Toolchains and linker inputs sizes do not match." );
3050
3144
3051
- // Append a new link action for each device.
3052
- auto TC = ToolChains.begin ();
3053
- for (auto &LI : DeviceLinkerInputs) {
3054
- auto *DeviceLinkAction =
3055
- C.MakeAction <LinkJobAction>(LI, types::TY_Image);
3056
-
3057
- // After the Link, wrap the files before the final host link
3058
- auto *DeviceWrappingAction =
3059
- C.MakeAction <OffloadWrappingJobAction>(DeviceLinkAction,
3060
- types::TY_Object);
3061
- DA.add (*DeviceWrappingAction, **TC, /* BoundArch=*/ nullptr ,
3062
- Action::OFK_SYCL);
3063
- ++TC;
3145
+ // FIXME - If -fsycl-add-targets is provided, do not link in the regular
3146
+ // device binaries - only pull in the add-targets variants. We are doing
3147
+ // this to allow for a specific device only binary to be created until
3148
+ // we have the ability to resolve multiple devices
3149
+ if (SYCLAOTInputs.empty ()) {
3150
+ // Append a new link action for each device.
3151
+ auto TC = ToolChains.begin ();
3152
+ for (auto &LI : DeviceLinkerInputs) {
3153
+ auto *DeviceLinkAction =
3154
+ C.MakeAction <LinkJobAction>(LI, types::TY_Image);
3155
+
3156
+ // After the Link, wrap the files before the final host link
3157
+ auto *DeviceWrappingAction =
3158
+ C.MakeAction <OffloadWrappingJobAction>(DeviceLinkAction,
3159
+ types::TY_Object);
3160
+ DA.add (*DeviceWrappingAction, **TC, /* BoundArch=*/ nullptr ,
3161
+ Action::OFK_SYCL);
3162
+ ++TC;
3163
+ }
3164
+ } else {
3165
+ // Perform additional wraps against -fsycl-add-targets
3166
+ // FIXME - The triple is currently not used from the AOT inputs, these
3167
+ // will eventually be added to a manifest that is built into the final
3168
+ // binary
3169
+ ActionList AddInputs;
3170
+ for (auto SAI : SYCLAOTInputs) {
3171
+ std::string FN (SAI.second );
3172
+ const char * FNStr = Args.MakeArgString (FN);
3173
+ Arg *myArg = Args.MakeSeparateArg (nullptr ,
3174
+ C.getDriver ().getOpts ().getOption (options::OPT_INPUT), FNStr);
3175
+ Action *SYCLAdd = C.MakeAction <InputAction>(*myArg,
3176
+ types::TY_SYCL_FATBIN);
3177
+ AddInputs.push_back (SYCLAdd);
3178
+ }
3179
+ for (auto &LI : AddInputs) {
3180
+ auto *DeviceWrappingAction =
3181
+ C.MakeAction <OffloadWrappingJobAction>(LI, types::TY_Object);
3182
+ DA.add (*DeviceWrappingAction, *ToolChains.front (),
3183
+ /* BoundArch=*/ nullptr , Action::OFK_SYCL);
3184
+ }
3064
3185
}
3065
3186
}
3066
3187
@@ -3072,6 +3193,36 @@ class OffloadingActionBuilder final {
3072
3193
++TI)
3073
3194
ToolChains.push_back (TI->second );
3074
3195
3196
+ Arg *SYCLLinkTargets = Args.getLastArg (
3197
+ options::OPT_fsycl_link_targets_EQ);
3198
+ CompileDeviceOnly = SYCLLinkTargets &&
3199
+ SYCLLinkTargets->getOption ().matches (
3200
+ options::OPT_fsycl_link_targets_EQ);
3201
+ Arg *SYCLAddTargets = Args.getLastArg (
3202
+ options::OPT_fsycl_add_targets_EQ);
3203
+ if (SYCLAddTargets) {
3204
+ for (StringRef Val : SYCLAddTargets->getValues ()) {
3205
+ // Parse out the Triple and Input (triple:binary) and create a
3206
+ // ToolChain for each entry. Each of these will be wrapped and fed
3207
+ // into the final binary
3208
+ // Populate the pairs, expects format of 'triple:file', any other
3209
+ // format will not be accepted
3210
+ std::pair<StringRef, StringRef> I = Val.split (' :' );
3211
+ llvm::Triple TT;
3212
+ const char * TF;
3213
+ if (!I.first .empty () && !I.second .empty ()) {
3214
+ TT = llvm::Triple (I.first );
3215
+ TF = C.getArgs ().MakeArgString (I.second );
3216
+ // populate the input vector
3217
+ SYCLAOTInputs.push_back (std::make_pair (TT, TF));
3218
+ } else {
3219
+ // No colon found, do not use the input
3220
+ C.getDriver ().Diag (diag::err_drv_unsupported_option_argument)
3221
+ << SYCLAddTargets->getOption ().getName () << Val;
3222
+ }
3223
+ }
3224
+ }
3225
+
3075
3226
DeviceLinkerInputs.resize (ToolChains.size ());
3076
3227
return false ;
3077
3228
}
@@ -4324,9 +4475,18 @@ InputInfo Driver::BuildJobsForActionNoCache(
4324
4475
Arch = UI.DependentBoundArch ;
4325
4476
} else
4326
4477
Arch = BoundArch;
4478
+ // When unbundling for SYCL and there is no Target offload, assume
4479
+ // Host as the dependent offload, as the host path has been stripped
4480
+ // in this instance
4481
+ Action::OffloadKind DependentOffloadKind;
4482
+ if (UI.DependentOffloadKind == Action::OFK_SYCL &&
4483
+ TargetDeviceOffloadKind == Action::OFK_None)
4484
+ DependentOffloadKind = Action::OFK_Host;
4485
+ else
4486
+ DependentOffloadKind = UI.DependentOffloadKind ;
4327
4487
4328
4488
CachedResults[{A, GetTriplePlusArchString (UI.DependentToolChain , Arch,
4329
- UI. DependentOffloadKind )}] =
4489
+ DependentOffloadKind)}] =
4330
4490
CurI;
4331
4491
}
4332
4492
@@ -4340,12 +4500,20 @@ InputInfo Driver::BuildJobsForActionNoCache(
4340
4500
} else if (JA->getType () == types::TY_Nothing)
4341
4501
Result = InputInfo (A, BaseInput);
4342
4502
else {
4343
- // We only have to generate a prefix for the host if this is not a top-level
4344
- // action.
4345
- std::string OffloadingPrefix = Action::GetOffloadingFileNamePrefix (
4503
+ std::string OffloadingPrefix;
4504
+ // When generating binaries with -fsycl-link-target, the output file prefix
4505
+ // is the triple arch only
4506
+ if (Args.getLastArg (options::OPT_fsycl_link_targets_EQ)) {
4507
+ OffloadingPrefix = " -" ;
4508
+ OffloadingPrefix += TC->getTriple ().getArchName ();
4509
+ } else {
4510
+ // We only have to generate a prefix for the host if this is not a
4511
+ // top-level action.
4512
+ OffloadingPrefix = Action::GetOffloadingFileNamePrefix (
4346
4513
A->getOffloadingDeviceKind (), TC->getTriple ().normalize (),
4347
4514
/* CreatePrefixForHost=*/ !!A->getOffloadingHostActiveKinds () &&
4348
4515
!AtTopLevel);
4516
+ }
4349
4517
Result = InputInfo (A, GetNamedOutputPath (C, *JA, BaseInput, BoundArch,
4350
4518
AtTopLevel, MultipleArchs,
4351
4519
OffloadingPrefix),
0 commit comments