Index: include/clang/AST/Decl.h =================================================================== --- include/clang/AST/Decl.h +++ include/clang/AST/Decl.h @@ -826,6 +826,10 @@ // Second check is for C++11 [dcl.stc]p4. return !isFileVarDecl() && getTSCSpec() == TSCS_unspecified; + // Global Named Register (GNU extension) + if (getStorageClass() == SC_Register && !isLocalVarDecl()) + return false; + // Return true for: Auto, Register. // Return false for: Extern, Static, PrivateExtern, OpenCLWorkGroupLocal. Index: lib/CodeGen/CGExpr.cpp =================================================================== --- lib/CodeGen/CGExpr.cpp +++ lib/CodeGen/CGExpr.cpp @@ -21,6 +21,7 @@ #include "TargetInfo.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/Attr.h" #include "clang/Frontend/CodeGenOptions.h" #include "llvm/ADT/Hashing.h" #include "llvm/IR/DataLayout.h" @@ -1276,6 +1277,10 @@ if (LV.isExtVectorElt()) return EmitLoadOfExtVectorElementLValue(LV); + // Global Register variables always invoke intrinsics + if (LV.isGlobalReg()) + return EmitLoadOfGlobalRegLValue(LV); + assert(LV.isBitField() && "Unknown LValue type!"); return EmitLoadOfBitfieldLValue(LV); } @@ -1343,6 +1348,17 @@ return RValue::get(Vec); } +/// EmitLoadOfGlobalRegLValue - Load of Global Named Registers are always calls +/// to read_register intrinsics. +RValue CodeGenFunction::EmitLoadOfGlobalRegLValue(LValue LV) { + assert(LV.getType()->isIntegerType() && "Bad type for register variable"); + llvm::MDNode *RegName = dyn_cast(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 @@ -1350,6 +1366,9 @@ /// is 'Ty'. void CodeGenFunction::EmitStoreThroughLValue(RValue Src, LValue Dst, bool isInit) { + if (Dst.isGlobalReg()) + return EmitStoreThroughGlobalRegLValue(Src, Dst); + if (!Dst.isSimple()) { if (Dst.isVectorElt()) { // Read/modify/write the vector, inserting the new element. @@ -1581,6 +1600,18 @@ Store->setAlignment(Dst.getAlignment().getQuantity()); } +/// EmitStoreThroughGlobalRegLValue - Load of Global Named Registers are always +/// calls to read_register intrinsics. +void CodeGenFunction::EmitStoreThroughGlobalRegLValue(RValue Src, LValue Dst) { + assert(Dst.getType()->isIntegerType() && "Bad type for register variable"); + llvm::MDNode *RegName = dyn_cast(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 // generating write-barries API. It is currently a global, ivar, // or neither. @@ -1740,14 +1771,41 @@ 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(); + llvm::MDString *Str = llvm::MDString::get(CGM.getLLVMContext(), + Asm->getLabel()); + llvm::Twine Name("llvm.named.register."+Asm->getLabel()); + llvm::NamedMDNode* M = CGM.getModule().getOrInsertNamedMetadata(Name.str()); + if (M->getNumOperands() == 0) { + 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) { const NamedDecl *ND = E->getDecl(); CharUnits Alignment = getContext().getDeclAlign(ND); QualType T = E->getType(); + const auto *VD = dyn_cast(ND); + + // Global Named registers access via intrinsics only + if (VD && VD->getStorageClass() == SC_Register && + VD->hasAttr() && !VD->isLocalVarDecl()) + return EmitGlobalNamedRegister(VD, CGM, Alignment); // A DeclRefExpr for a reference initialized by a constant expression can // appear without being odr-used. Directly emit the constant initializer. - if (const auto *VD = dyn_cast(ND)) { + if (VD) { const Expr *Init = VD->getAnyInitializer(VD); if (Init && !isa(VD) && VD->getType()->isReferenceType() && VD->isUsableInConstantExpressions(getContext()) && @@ -1773,7 +1831,7 @@ return MakeAddrLValue(Aliasee, T, Alignment); } - if (const auto *VD = dyn_cast(ND)) { + if (VD) { // Check if this is a global variable. if (VD->hasLinkage() || VD->isStaticDataMember()) return EmitGlobalVarDeclLValue(*this, E, VD); Index: lib/CodeGen/CGValue.h =================================================================== --- lib/CodeGen/CGValue.h +++ lib/CodeGen/CGValue.h @@ -110,7 +110,8 @@ Simple, // This is a normal l-value, use getAddress(). VectorElt, // This is a vector element l-value (V[i]), use getVector* 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; llvm::Value *V; @@ -192,6 +193,7 @@ bool isVectorElt() const { return LVType == VectorElt; } bool isBitField() const { return LVType == BitField; } bool isExtVectorElt() const { return LVType == ExtVectorElt; } + bool isGlobalReg() const { return LVType == GlobalReg; } bool isVolatileQualified() const { return Quals.hasVolatile(); } bool isRestrictQualified() const { return Quals.hasRestrict(); } @@ -286,6 +288,9 @@ return *BitFieldInfo; } + // global register lvalue + llvm::Value *getGlobalReg() const { assert(isGlobalReg()); return V; } + static LValue MakeAddr(llvm::Value *address, QualType type, CharUnits alignment, ASTContext &Context, llvm::MDNode *TBAAInfo = 0) { @@ -336,6 +341,16 @@ 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 { // FIMXE: Alignment return RValue::getAggregate(getAddress(), isVolatileQualified()); Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -1979,12 +1979,14 @@ RValue EmitLoadOfLValue(LValue V, SourceLocation Loc); RValue EmitLoadOfExtVectorElementLValue(LValue V); RValue EmitLoadOfBitfieldLValue(LValue LV); + RValue EmitLoadOfGlobalRegLValue(LValue LV); /// EmitStoreThroughLValue - Store the specified rvalue into the specified /// lvalue, where both are guaranteed to the have the same type, and that type /// is 'Ty'. void EmitStoreThroughLValue(RValue Src, LValue Dst, bool isInit=false); void EmitStoreThroughExtVectorComponentLValue(RValue Src, LValue Dst); + void EmitStoreThroughGlobalRegLValue(RValue Src, LValue Dst); /// EmitStoreThroughBitfieldLValue - Store Src into Dst with same constraints /// as EmitStoreThroughLValue. @@ -2009,6 +2011,7 @@ // Note: only available for agg return types LValue EmitVAArgExprLValue(const VAArgExpr *E); LValue EmitDeclRefLValue(const DeclRefExpr *E); + LValue EmitReadRegister(const VarDecl *VD); LValue EmitStringLiteralLValue(const StringLiteral *E); LValue EmitObjCEncodeExprLValue(const ObjCEncodeExpr *E); LValue EmitPredefinedLValue(const PredefinedExpr *E); Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -5119,13 +5119,9 @@ if (!DC->isRecord() && S->getFnParent() == 0) { // C99 6.9p2: The storage-class specifiers auto and register shall not // 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(); } } @@ -5437,6 +5433,7 @@ Diag(E->getExprLoc(), diag::warn_asm_label_on_auto_decl) << Label; break; case SC_Register: + // Local Named register if (!Context.getTargetInfo().isValidGCCRegisterName(Label)) Diag(E->getExprLoc(), diag::err_asm_unknown_register_name) << Label; break; @@ -5446,6 +5443,10 @@ case SC_OpenCLWorkGroupLocal: 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), Index: test/CodeGen/named_reg_global.c =================================================================== --- /dev/null +++ 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"} Index: test/Sema/asm.c =================================================================== --- test/Sema/asm.c +++ test/Sema/asm.c @@ -95,8 +95,6 @@ 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){ static int g asm ("g_asm") = 0; extern int gg asm ("gg_asm"); Index: test/Sema/decl-invalid.c =================================================================== --- test/Sema/decl-invalid.c +++ test/Sema/decl-invalid.c @@ -24,5 +24,4 @@ // rdar://6880449 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}}