Add support for the the target attribute.
authorEric Christopher <echristo@gmail.com>
Fri, 12 Jun 2015 01:35:52 +0000 (01:35 +0000)
committerEric Christopher <echristo@gmail.com>
Fri, 12 Jun 2015 01:35:52 +0000 (01:35 +0000)
Modeled after the gcc attribute of the same name, this feature
allows source level annotations to correspond to backend code
generation. In llvm particular parlance, this allows the adding
of subtarget features and changing the cpu for a particular function
based on source level hints.

This has been added into the existing support for function level
attributes without particular verification for any target outside
of whether or not the backend will support the features/cpu given
(similar to section, etc).

llvm-svn: 239579

clang/include/clang/Basic/Attr.td
clang/lib/CodeGen/CGCall.cpp
clang/lib/Sema/SemaDeclAttr.cpp
clang/test/CodeGen/attr-target.c [new file with mode: 0644]
clang/test/Sema/attr-target.c [new file with mode: 0644]

index bcc230a..4c0e56b 100644 (file)
@@ -1250,6 +1250,14 @@ def Pascal : InheritableAttr {
   let Documentation = [Undocumented];
 }
 
+def Target : InheritableAttr {
+  let Spellings = [GCC<"target">];
+  let Args = [StringArgument<"features">];
+  let Subjects =
+      SubjectList<[Function], ErrorDiag, "ExpectedFunctionMethodOrClass">;
+  let Documentation = [Undocumented];
+}
+
 def TransparentUnion : InheritableAttr {
   let Spellings = [GCC<"transparent_union">];
 //  let Subjects = SubjectList<[Record, TypedefName]>;
index c2e1e57..894391c 100644 (file)
@@ -1483,24 +1483,52 @@ void CodeGenModule::ConstructAttributeList(const CGFunctionInfo &FI,
     if (!CodeGenOpts.StackRealignment)
       FuncAttrs.addAttribute("no-realign-stack");
 
-    // Add target-cpu and target-features work if they differ from the defaults.
-    std::string &CPU = getTarget().getTargetOpts().CPU;
-    if (CPU != "")
-      FuncAttrs.addAttribute("target-cpu", CPU);
+    // Add target-cpu and target-features attributes to functions. If
+    // we have a decl for the function and it has a target attribute then
+    // parse that and add it to the feature set.
+    StringRef TargetCPU = getTarget().getTargetOpts().CPU;
 
     // TODO: Features gets us the features on the command line including
     // feature dependencies. For canonicalization purposes we might want to
-    // avoid putting features in the target-features set if we know it'll be one
-    // of the default features in the backend, e.g. corei7-avx and +avx or figure
-    // out non-explicit dependencies.
-    std::vector<std::string> &Features = getTarget().getTargetOpts().Features;
+    // avoid putting features in the target-features set if we know it'll be
+    // one of the default features in the backend, e.g. corei7-avx and +avx or
+    // figure out non-explicit dependencies.
+    std::vector<std::string> Features(getTarget().getTargetOpts().Features);
+
+    // TODO: The target attribute complicates this further by allowing multiple
+    // additional features to be tacked on to the feature string for a
+    // particular function. For now we simply append to the set of features and
+    // let backend resolution fix them up.
+    const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(TargetDecl);
+    if (FD) {
+      if (const TargetAttr *TD = FD->getAttr<TargetAttr>()) {
+        StringRef FeaturesStr = TD->getFeatures();
+        SmallVector<StringRef, 1> AttrFeatures;
+        FeaturesStr.split(AttrFeatures, ",");
+
+        // Grab the various features and prepend a "+" to turn on the feature to
+        // the backend and add them to our existing set of Features.
+        for (auto &Feature : AttrFeatures) {
+          // While we're here iterating check for a different target cpu.
+          if (Feature.startswith("arch="))
+            TargetCPU = Feature.split("=").second;
+         else
+           Features.push_back("+" + Feature.str());
+       }
+      }
+    }
+
+    // Now add the target-cpu and target-features to the function.
+    if (TargetCPU != "")
+      FuncAttrs.addAttribute("target-cpu", TargetCPU);
     if (!Features.empty()) {
-      std::stringstream S;
+      std::stringstream TargetFeatures;
       std::copy(Features.begin(), Features.end(),
-                std::ostream_iterator<std::string>(S, ","));
+                std::ostream_iterator<std::string>(TargetFeatures, ","));
+
       // The drop_back gets rid of the trailing space.
       FuncAttrs.addAttribute("target-features",
-                             StringRef(S.str()).drop_back(1));
+                             StringRef(TargetFeatures.str()).drop_back(1));
     }
   }
 
index 1d04159..5c5321e 100644 (file)
@@ -2397,6 +2397,20 @@ static void handleSectionAttr(Sema &S, Decl *D, const AttributeList &Attr) {
     D->addAttr(NewAttr);
 }
 
+static void handleTargetAttr(Sema &S, Decl *D, const AttributeList &Attr) {
+  // TODO: Validation should use a backend target library that specifies
+  // the allowable subtarget features and cpus. We could use something like a
+  // TargetCodeGenInfo hook here to do validation.
+  StringRef Str;
+  SourceLocation LiteralLoc;
+  if (!S.checkStringLiteralArgumentAttr(Attr, 0, Str, &LiteralLoc))
+    return;
+  unsigned Index = Attr.getAttributeSpellingListIndex();
+  TargetAttr *NewAttr =
+      ::new (S.Context) TargetAttr(Attr.getRange(), S.Context, Str, Index);
+  D->addAttr(NewAttr);
+}
+
 
 static void handleCleanupAttr(Sema &S, Decl *D, const AttributeList &Attr) {
   VarDecl *VD = cast<VarDecl>(D);
@@ -4716,6 +4730,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
   case AttributeList::AT_Section:
     handleSectionAttr(S, D, Attr);
     break;
+  case AttributeList::AT_Target:
+    handleTargetAttr(S, D, Attr);
+    break;
   case AttributeList::AT_Unavailable:
     handleAttrWithMessage<UnavailableAttr>(S, D, Attr);
     break;
diff --git a/clang/test/CodeGen/attr-target.c b/clang/test/CodeGen/attr-target.c
new file mode 100644 (file)
index 0000000..dbf00d7
--- /dev/null
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -target-cpu x86-64 -emit-llvm %s -o - | FileCheck %s
+
+int baz(int a) { return 4; }
+
+int __attribute__((target("avx,sse4.2,arch=ivybridge"))) foo(int a) { return 4; }
+
+int bar(int a) { return baz(a) + foo(a); }
+
+// Check that we emit the additional subtarget and cpu features for foo and not for baz or bar.
+// CHECK: baz{{.*}} #0
+// CHECK: foo{{.*}} #1
+// CHECK: bar{{.*}} #0
+// CHECK: #0 = {{.*}}"target-cpu"="x86-64" "target-features"="+sse,+sse2"
+// CHECK: #1 = {{.*}}"target-cpu"="ivybridge" "target-features"="+sse,+sse2,+avx,+sse4.2"
diff --git a/clang/test/Sema/attr-target.c b/clang/test/Sema/attr-target.c
new file mode 100644 (file)
index 0000000..da3b374
--- /dev/null
@@ -0,0 +1,6 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu  -fsyntax-only -verify %s
+
+int __attribute__((target("avx,sse4.2,arch=ivybridge"))) foo() { return 4; }
+int __attribute__((target())) bar() { return 4; } //expected-error {{'target' attribute takes one argument}}
+
+