Эх сурвалжийг харах

Non-allocatable Global Named Register

This patch implements global named registers in Clang, lowering to the just
created intrinsics in LLVM (@llvm.read/write_register). A new type of LValue
had to be created (Register), which just adds support to carry the metadata
node containing the name of the register. Two new methods to emit loads and
stores interoperate with another to emit the named metadata node.

No guarantees are being made and only non-allocatable global variable named
registers are being supported. Local named register support is unchanged.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@209149 91177308-0d34-0410-b5e6-96231b3b80d8
Renato Golin 11 жил өмнө
parent
commit
5df2310080

+ 4 - 0
include/clang/AST/Decl.h

@@ -826,6 +826,10 @@ public:
       // Second check is for C++11 [dcl.stc]p4.
       // Second check is for C++11 [dcl.stc]p4.
       return !isFileVarDecl() && getTSCSpec() == TSCS_unspecified;
       return !isFileVarDecl() && getTSCSpec() == TSCS_unspecified;
 
 
+    // Global Named Register (GNU extension)
+    if (getStorageClass() == SC_Register && !isLocalVarDecl())
+      return false;
+
     // Return true for:  Auto, Register.
     // Return true for:  Auto, Register.
     // Return false for: Extern, Static, PrivateExtern, OpenCLWorkGroupLocal.
     // Return false for: Extern, Static, PrivateExtern, OpenCLWorkGroupLocal.
 
 

+ 58 - 2
lib/CodeGen/CGExpr.cpp

@@ -21,6 +21,7 @@
 #include "TargetInfo.h"
 #include "TargetInfo.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/DeclObjC.h"
+#include "clang/AST/Attr.h"
 #include "clang/Frontend/CodeGenOptions.h"
 #include "clang/Frontend/CodeGenOptions.h"
 #include "llvm/ADT/Hashing.h"
 #include "llvm/ADT/Hashing.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/DataLayout.h"
@@ -1276,6 +1277,10 @@ RValue CodeGenFunction::EmitLoadOfLValue(LValue LV, SourceLocation Loc) {
   if (LV.isExtVectorElt())
   if (LV.isExtVectorElt())
     return EmitLoadOfExtVectorElementLValue(LV);
     return EmitLoadOfExtVectorElementLValue(LV);
 
 
+  // Global Register variables always invoke intrinsics
+  if (LV.isGlobalReg())
+    return EmitLoadOfGlobalRegLValue(LV);
+
   assert(LV.isBitField() && "Unknown LValue type!");
   assert(LV.isBitField() && "Unknown LValue type!");
   return EmitLoadOfBitfieldLValue(LV);
   return EmitLoadOfBitfieldLValue(LV);
 }
 }
@@ -1343,6 +1348,16 @@ RValue CodeGenFunction::EmitLoadOfExtVectorElementLValue(LValue LV) {
   return RValue::get(Vec);
   return RValue::get(Vec);
 }
 }
 
 
+/// @brief Load of global gamed gegisters are always calls to intrinsics.
+RValue CodeGenFunction::EmitLoadOfGlobalRegLValue(LValue LV) {
+  assert(LV.getType()->isIntegerType() && "Bad type for register variable");
+  llvm::MDNode *RegName = dyn_cast<llvm::MDNode>(LV.getGlobalReg());
+  assert(RegName && "Register LValue is not metadata");
+  llvm::Type *Types[] = { CGM.getTypes().ConvertType(LV.getType()) };
+  llvm::Value *F = CGM.getIntrinsic(llvm::Intrinsic::read_register, Types);
+  llvm::Value* Call = Builder.CreateCall(F, RegName);
+  return RValue::get(Call);
+}
 
 
 
 
 /// EmitStoreThroughLValue - Store the specified rvalue into the specified
 /// EmitStoreThroughLValue - Store the specified rvalue into the specified
@@ -1370,6 +1385,9 @@ void CodeGenFunction::EmitStoreThroughLValue(RValue Src, LValue Dst,
     if (Dst.isExtVectorElt())
     if (Dst.isExtVectorElt())
       return EmitStoreThroughExtVectorComponentLValue(Src, Dst);
       return EmitStoreThroughExtVectorComponentLValue(Src, Dst);
 
 
+    if (Dst.isGlobalReg())
+      return EmitStoreThroughGlobalRegLValue(Src, Dst);
+
     assert(Dst.isBitField() && "Unknown LValue type");
     assert(Dst.isBitField() && "Unknown LValue type");
     return EmitStoreThroughBitfieldLValue(Src, Dst);
     return EmitStoreThroughBitfieldLValue(Src, Dst);
   }
   }
@@ -1581,6 +1599,17 @@ void CodeGenFunction::EmitStoreThroughExtVectorComponentLValue(RValue Src,
   Store->setAlignment(Dst.getAlignment().getQuantity());
   Store->setAlignment(Dst.getAlignment().getQuantity());
 }
 }
 
 
+/// @brief Store of global named registers are always calls to intrinsics.
+void CodeGenFunction::EmitStoreThroughGlobalRegLValue(RValue Src, LValue Dst) {
+  assert(Dst.getType()->isIntegerType() && "Bad type for register variable");
+  llvm::MDNode *RegName = dyn_cast<llvm::MDNode>(Dst.getGlobalReg());
+  assert(RegName && "Register LValue is not metadata");
+  llvm::Type *Types[] = { CGM.getTypes().ConvertType(Dst.getType()) };
+  llvm::Value *F = CGM.getIntrinsic(llvm::Intrinsic::write_register, Types);
+  llvm::Value *Value = Src.getScalarVal();
+  Builder.CreateCall2(F, RegName, Value);
+}
+
 // setObjCGCLValueClass - sets class of he lvalue for the purpose of
 // setObjCGCLValueClass - sets class of he lvalue for the purpose of
 // generating write-barries API. It is currently a global, ivar,
 // generating write-barries API. It is currently a global, ivar,
 // or neither.
 // or neither.
@@ -1740,14 +1769,41 @@ static LValue EmitCapturedFieldLValue(CodeGenFunction &CGF, const FieldDecl *FD,
   return CGF.EmitLValueForField(LV, FD);
   return CGF.EmitLValueForField(LV, FD);
 }
 }
 
 
+/// Named Registers are named metadata pointing to the register name
+/// which will be read from/written to as an argument to the intrinsic
+/// @llvm.read/write_register.
+/// So far, only the name is being passed down, but other options such as
+/// register type, allocation type or even optimization options could be
+/// passed down via the metadata node.
+static LValue EmitGlobalNamedRegister(const VarDecl *VD,
+                                      CodeGenModule &CGM,
+                                      CharUnits Alignment) {
+  AsmLabelAttr *Asm = VD->getAttr<AsmLabelAttr>();
+  llvm::Twine Name("llvm.named.register."+Asm->getLabel());
+  llvm::NamedMDNode *M = CGM.getModule().getOrInsertNamedMetadata(Name.str());
+  if (M->getNumOperands() == 0) {
+    llvm::MDString *Str = llvm::MDString::get(CGM.getLLVMContext(),
+                                              Asm->getLabel());
+    llvm::Value *Ops[] = { Str };
+    M->addOperand(llvm::MDNode::get(CGM.getLLVMContext(), Ops));
+  }
+  return LValue::MakeGlobalReg(M->getOperand(0), VD->getType(), Alignment);
+}
+
 LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) {
 LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) {
   const NamedDecl *ND = E->getDecl();
   const NamedDecl *ND = E->getDecl();
   CharUnits Alignment = getContext().getDeclAlign(ND);
   CharUnits Alignment = getContext().getDeclAlign(ND);
   QualType T = E->getType();
   QualType T = E->getType();
+  const auto *VD = dyn_cast<VarDecl>(ND);
+
+  // Global Named registers access via intrinsics only
+  if (VD && VD->getStorageClass() == SC_Register &&
+       VD->hasAttr<AsmLabelAttr>() && !VD->isLocalVarDecl())
+    return EmitGlobalNamedRegister(VD, CGM, Alignment);
 
 
   // A DeclRefExpr for a reference initialized by a constant expression can
   // A DeclRefExpr for a reference initialized by a constant expression can
   // appear without being odr-used. Directly emit the constant initializer.
   // appear without being odr-used. Directly emit the constant initializer.
-  if (const auto *VD = dyn_cast<VarDecl>(ND)) {
+  if (VD) {
     const Expr *Init = VD->getAnyInitializer(VD);
     const Expr *Init = VD->getAnyInitializer(VD);
     if (Init && !isa<ParmVarDecl>(VD) && VD->getType()->isReferenceType() &&
     if (Init && !isa<ParmVarDecl>(VD) && VD->getType()->isReferenceType() &&
         VD->isUsableInConstantExpressions(getContext()) &&
         VD->isUsableInConstantExpressions(getContext()) &&
@@ -1773,7 +1829,7 @@ LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) {
     return MakeAddrLValue(Aliasee, T, Alignment);
     return MakeAddrLValue(Aliasee, T, Alignment);
   }
   }
 
 
-  if (const auto *VD = dyn_cast<VarDecl>(ND)) {
+  if (VD) {
     // Check if this is a global variable.
     // Check if this is a global variable.
     if (VD->hasLinkage() || VD->isStaticDataMember())
     if (VD->hasLinkage() || VD->isStaticDataMember())
       return EmitGlobalVarDeclLValue(*this, E, VD);
       return EmitGlobalVarDeclLValue(*this, E, VD);

+ 16 - 1
lib/CodeGen/CGValue.h

@@ -110,7 +110,8 @@ class LValue {
     Simple,       // This is a normal l-value, use getAddress().
     Simple,       // This is a normal l-value, use getAddress().
     VectorElt,    // This is a vector element l-value (V[i]), use getVector*
     VectorElt,    // This is a vector element l-value (V[i]), use getVector*
     BitField,     // This is a bitfield l-value, use getBitfield*.
     BitField,     // This is a bitfield l-value, use getBitfield*.
-    ExtVectorElt  // This is an extended vector subset, use getExtVectorComp
+    ExtVectorElt, // This is an extended vector subset, use getExtVectorComp
+    GlobalReg     // This is a register l-value, use getGlobalReg()
   } LVType;
   } LVType;
 
 
   llvm::Value *V;
   llvm::Value *V;
@@ -192,6 +193,7 @@ public:
   bool isVectorElt() const { return LVType == VectorElt; }
   bool isVectorElt() const { return LVType == VectorElt; }
   bool isBitField() const { return LVType == BitField; }
   bool isBitField() const { return LVType == BitField; }
   bool isExtVectorElt() const { return LVType == ExtVectorElt; }
   bool isExtVectorElt() const { return LVType == ExtVectorElt; }
+  bool isGlobalReg() const { return LVType == GlobalReg; }
 
 
   bool isVolatileQualified() const { return Quals.hasVolatile(); }
   bool isVolatileQualified() const { return Quals.hasVolatile(); }
   bool isRestrictQualified() const { return Quals.hasRestrict(); }
   bool isRestrictQualified() const { return Quals.hasRestrict(); }
@@ -286,6 +288,9 @@ public:
     return *BitFieldInfo;
     return *BitFieldInfo;
   }
   }
 
 
+  // global register lvalue
+  llvm::Value *getGlobalReg() const { assert(isGlobalReg()); return V; }
+
   static LValue MakeAddr(llvm::Value *address, QualType type,
   static LValue MakeAddr(llvm::Value *address, QualType type,
                          CharUnits alignment, ASTContext &Context,
                          CharUnits alignment, ASTContext &Context,
                          llvm::MDNode *TBAAInfo = 0) {
                          llvm::MDNode *TBAAInfo = 0) {
@@ -336,6 +341,16 @@ public:
     return R;
     return R;
   }
   }
 
 
+  static LValue MakeGlobalReg(llvm::Value *Reg,
+                              QualType type,
+                              CharUnits Alignment) {
+    LValue R;
+    R.LVType = GlobalReg;
+    R.V = Reg;
+    R.Initialize(type, type.getQualifiers(), Alignment);
+    return R;
+  }
+
   RValue asAggregateRValue() const {
   RValue asAggregateRValue() const {
     // FIMXE: Alignment
     // FIMXE: Alignment
     return RValue::getAggregate(getAddress(), isVolatileQualified());
     return RValue::getAggregate(getAddress(), isVolatileQualified());

+ 3 - 0
lib/CodeGen/CodeGenFunction.h

@@ -1979,12 +1979,14 @@ public:
   RValue EmitLoadOfLValue(LValue V, SourceLocation Loc);
   RValue EmitLoadOfLValue(LValue V, SourceLocation Loc);
   RValue EmitLoadOfExtVectorElementLValue(LValue V);
   RValue EmitLoadOfExtVectorElementLValue(LValue V);
   RValue EmitLoadOfBitfieldLValue(LValue LV);
   RValue EmitLoadOfBitfieldLValue(LValue LV);
+  RValue EmitLoadOfGlobalRegLValue(LValue LV);
 
 
   /// EmitStoreThroughLValue - Store the specified rvalue into the specified
   /// EmitStoreThroughLValue - Store the specified rvalue into the specified
   /// lvalue, where both are guaranteed to the have the same type, and that type
   /// lvalue, where both are guaranteed to the have the same type, and that type
   /// is 'Ty'.
   /// is 'Ty'.
   void EmitStoreThroughLValue(RValue Src, LValue Dst, bool isInit=false);
   void EmitStoreThroughLValue(RValue Src, LValue Dst, bool isInit=false);
   void EmitStoreThroughExtVectorComponentLValue(RValue Src, LValue Dst);
   void EmitStoreThroughExtVectorComponentLValue(RValue Src, LValue Dst);
+  void EmitStoreThroughGlobalRegLValue(RValue Src, LValue Dst);
 
 
   /// EmitStoreThroughBitfieldLValue - Store Src into Dst with same constraints
   /// EmitStoreThroughBitfieldLValue - Store Src into Dst with same constraints
   /// as EmitStoreThroughLValue.
   /// as EmitStoreThroughLValue.
@@ -2009,6 +2011,7 @@ public:
   // Note: only available for agg return types
   // Note: only available for agg return types
   LValue EmitVAArgExprLValue(const VAArgExpr *E);
   LValue EmitVAArgExprLValue(const VAArgExpr *E);
   LValue EmitDeclRefLValue(const DeclRefExpr *E);
   LValue EmitDeclRefLValue(const DeclRefExpr *E);
+  LValue EmitReadRegister(const VarDecl *VD);
   LValue EmitStringLiteralLValue(const StringLiteral *E);
   LValue EmitStringLiteralLValue(const StringLiteral *E);
   LValue EmitObjCEncodeExprLValue(const ObjCEncodeExpr *E);
   LValue EmitObjCEncodeExprLValue(const ObjCEncodeExpr *E);
   LValue EmitPredefinedLValue(const PredefinedExpr *E);
   LValue EmitPredefinedLValue(const PredefinedExpr *E);

+ 8 - 7
lib/Sema/SemaDecl.cpp

@@ -5119,13 +5119,9 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC,
   if (!DC->isRecord() && S->getFnParent() == 0) {
   if (!DC->isRecord() && S->getFnParent() == 0) {
     // C99 6.9p2: The storage-class specifiers auto and register shall not
     // C99 6.9p2: The storage-class specifiers auto and register shall not
     // appear in the declaration specifiers in an external declaration.
     // appear in the declaration specifiers in an external declaration.
-    if (SC == SC_Auto || SC == SC_Register) {
-      // If this is a register variable with an asm label specified, then this
-      // is a GNU extension.
-      if (SC == SC_Register && D.getAsmLabel())
-        Diag(D.getIdentifierLoc(), diag::err_unsupported_global_register);
-      else
-        Diag(D.getIdentifierLoc(), diag::err_typecheck_sclass_fscope);
+    // Global Register+Asm is a GNU extension we support.
+    if (SC == SC_Auto || (SC == SC_Register && !D.getAsmLabel())) {
+      Diag(D.getIdentifierLoc(), diag::err_typecheck_sclass_fscope);
       D.setInvalidType();
       D.setInvalidType();
     }
     }
   }
   }
@@ -5437,6 +5433,7 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC,
         Diag(E->getExprLoc(), diag::warn_asm_label_on_auto_decl) << Label;
         Diag(E->getExprLoc(), diag::warn_asm_label_on_auto_decl) << Label;
         break;
         break;
       case SC_Register:
       case SC_Register:
+        // Local Named register
         if (!Context.getTargetInfo().isValidGCCRegisterName(Label))
         if (!Context.getTargetInfo().isValidGCCRegisterName(Label))
           Diag(E->getExprLoc(), diag::err_asm_unknown_register_name) << Label;
           Diag(E->getExprLoc(), diag::err_asm_unknown_register_name) << Label;
         break;
         break;
@@ -5446,6 +5443,10 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC,
       case SC_OpenCLWorkGroupLocal:
       case SC_OpenCLWorkGroupLocal:
         break;
         break;
       }
       }
+    } else if (SC == SC_Register) {
+      // Global Named register
+      if (!Context.getTargetInfo().isValidGCCRegisterName(Label))
+        Diag(E->getExprLoc(), diag::err_asm_unknown_register_name) << Label;
     }
     }
 
 
     NewVD->addAttr(::new (Context) AsmLabelAttr(SE->getStrTokenLoc(0),
     NewVD->addAttr(::new (Context) AsmLabelAttr(SE->getStrTokenLoc(0),

+ 26 - 0
test/CodeGen/named_reg_global.c

@@ -0,0 +1,26 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -S -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -S -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple arm64-linux-gnu -S -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple armv7-linux-gnu -S -emit-llvm %s -o - | FileCheck %s
+
+register unsigned long current_stack_pointer asm("sp");
+
+// CHECK: define{{.*}} i[[bits:[0-9]+]] @get_stack_pointer_addr()
+// CHECK: [[ret:%[0-9]+]] = call i[[bits]] @llvm.read_register.i[[bits]](metadata !0)
+// CHECK: ret i[[bits]] [[ret]]
+unsigned long get_stack_pointer_addr() {
+  return current_stack_pointer;
+}
+// CHECK: declare{{.*}} i[[bits]] @llvm.read_register.i[[bits]](metadata)
+
+// CHECK: define{{.*}} void @set_stack_pointer_addr(i[[bits]] %addr) #0 {
+// CHECK: [[sto:%[0-9]+]] = load i[[bits]]* %addr
+// CHECK: call void @llvm.write_register.i[[bits]](metadata !0, i[[bits]] [[sto]])
+// CHECK: ret void
+void set_stack_pointer_addr(unsigned long addr) {
+  current_stack_pointer = addr;
+}
+// CHECK: declare{{.*}} void @llvm.write_register.i[[bits]](metadata, i[[bits]])
+
+// CHECK: !llvm.named.register.sp = !{!0}
+// CHECK: !0 = metadata !{metadata !"sp"}

+ 0 - 2
test/Sema/asm.c

@@ -95,8 +95,6 @@ void test9(int i) {
   asm("" : [foo] "=r" (i), "=r"(i) : "[foo]1"(i)); // expected-error{{invalid input constraint '[foo]1' in asm}}
   asm("" : [foo] "=r" (i), "=r"(i) : "[foo]1"(i)); // expected-error{{invalid input constraint '[foo]1' in asm}}
 }
 }
 
 
-register int g asm("dx"); // expected-error{{global register variables are not supported}}
-
 void test10(void){
 void test10(void){
   static int g asm ("g_asm") = 0;
   static int g asm ("g_asm") = 0;
   extern int gg asm ("gg_asm");
   extern int gg asm ("gg_asm");

+ 0 - 1
test/Sema/decl-invalid.c

@@ -24,5 +24,4 @@ I; // expected-warning {{declaration does not declare anything}}
 
 
 // rdar://6880449
 // rdar://6880449
 register int test1;     // expected-error {{illegal storage class on file-scoped variable}}
 register int test1;     // expected-error {{illegal storage class on file-scoped variable}}
-register int test2 __asm__("edi");  // expected-error {{global register variables are not supported}}