Index: include/clang/AST/Type.h =================================================================== --- include/clang/AST/Type.h +++ include/clang/AST/Type.h @@ -3574,6 +3574,7 @@ IsConsumed = 0x10, HasPassObjSize = 0x20, IsNoEscape = 0x40, + IsWriteOnly = 0x80, }; unsigned char Data = 0; @@ -3627,6 +3628,19 @@ return Copy; } + bool isWriteOnly() const { + return Data & IsWriteOnly; + } + + ExtParameterInfo withIsWriteOnly(bool WriteOnly) const { + ExtParameterInfo Copy = *this; + if (WriteOnly) + Copy.Data |= IsWriteOnly; + else + Copy.Data &= ~IsWriteOnly; + return Copy; + } + unsigned char getOpaqueValue() const { return Data; } static ExtParameterInfo getFromOpaqueValue(unsigned char data) { ExtParameterInfo result; Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -1781,6 +1781,12 @@ let Documentation = [PcsDocs]; } +def WriteOnly : Attr { + let Spellings = [Clang<"write_only">]; + let Subjects = SubjectList<[Function, ParmVar], ErrorDiag>; + let Documentation = [WriteOnlyDocs]; +} + def Pure : InheritableAttr { let Spellings = [GCC<"pure">]; let Documentation = [Undocumented]; Index: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -3459,6 +3459,34 @@ }]; } +def WriteOnlyDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``write_only`` attribute can be applied to a function or a parameter of +pointer or reference type. + +When applied to a function, it indicates that the function may write to, but +does not read from, memory. A write-only function always writes the same value +to the same memory location when called with the same set of arguments and +global state. + +When the ``write_only`` attribute is applied, function calls may be +hoisted or eliminated when memory analysis determines it to be safe. When +applied to a virtual function, the overridden functions are expected to +adhere to be write-only functions. + +If a function with this attribute reads from memory, the behavior of the +program is undefined. + +When applied to a parameter, it indicates that the function may write to but +does not read through this pointer argument (even though it may read from +memory that the pointer points to). + +If a function reads from a write-only pointer argument, the behavior is +undefined. + }]; +} + def ReinitializesDocs : Documentation { let Category = DocCatFunction; let Content = [{ Index: lib/CodeGen/CGCall.cpp =================================================================== --- lib/CodeGen/CGCall.cpp +++ lib/CodeGen/CGCall.cpp @@ -1856,6 +1856,9 @@ FuncAttrs.addAttribute(llvm::Attribute::NoReturn); } + if (TargetDecl->hasAttr()) + FuncAttrs.addAttribute(llvm::Attribute::WriteOnly); + // 'const', 'pure' and 'noalias' attributed functions are also nounwind. if (TargetDecl->hasAttr()) { FuncAttrs.addAttribute(llvm::Attribute::ReadNone); @@ -2131,6 +2134,9 @@ if (FI.getExtParameterInfo(ArgNo).isNoEscape()) Attrs.addAttribute(llvm::Attribute::NoCapture); + if (FI.getExtParameterInfo(ArgNo).isWriteOnly()) + Attrs.addAttribute(llvm::Attribute::WriteOnly); + if (Attrs.hasAttributes()) { unsigned FirstIRArg, NumIRArgs; std::tie(FirstIRArg, NumIRArgs) = IRFunctionArgs.getIRArgs(ArgNo); Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -1480,6 +1480,26 @@ AL.getRange(), S.Context, AL.getAttributeSpellingListIndex())); } +static void handleWriteOnlyAttrParameter(Sema &S, Decl *D, + const ParsedAttr &AL) { + if (D->isInvalidDecl()) + return; + + // write_only only applies to pointer types when applied to an argument. + if (isa(D)) { + QualType T = cast(D)->getType(); + if (!S.isValidPointerAttrType(T, /* RefOkay */ true)) { + S.Diag(AL.getLoc(), diag::warn_attribute_pointer_or_reference_only) + << AL << AL.getRange() << 0; + return; + } + } + + D->addAttr(::new (S.Context) WriteOnlyAttr( + AL.getRange(), S.Context, AL.getAttributeSpellingListIndex())); + +} + static void handleAssumeAlignedAttr(Sema &S, Decl *D, const ParsedAttr &AL) { Expr *E = AL.getArgAsExpr(0), *OE = AL.getNumArgs() > 1 ? AL.getArgAsExpr(1) : nullptr; @@ -6372,6 +6392,9 @@ case ParsedAttr::AT_Sentinel: handleSentinelAttr(S, D, AL); break; + case ParsedAttr::AT_WriteOnly: + handleWriteOnlyAttrParameter(S, D, AL); + break; case ParsedAttr::AT_Const: handleSimpleAttribute(S, D, AL); break; Index: lib/Sema/SemaType.cpp =================================================================== --- lib/Sema/SemaType.cpp +++ lib/Sema/SemaType.cpp @@ -4759,6 +4759,11 @@ HasAnyInterestingExtParameterInfos = true; } + if (Param->hasAttr()) { + ExtParameterInfos[i] = ExtParameterInfos[i].withIsWriteOnly(true); + HasAnyInterestingExtParameterInfos = true; + } + ParamTys.push_back(ParamTy); } Index: test/CodeGen/attr-writeonly.cpp =================================================================== --- test/CodeGen/attr-writeonly.cpp +++ test/CodeGen/attr-writeonly.cpp @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm -disable-llvm-passes -Os -o - %s | FileCheck %s +// // RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm -disable-llvm-passes -Os -o - %s | FileCheck %s +// // RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -disable-llvm-passes -Os -o - %s | FileCheck %/s + +class TestClass { + public: + void setI(int j) __attribute__((write_only)); + private: + int I; +}; + +// CHECK-LABEL: define void @_Z5test1i(i32 %a) +// CHECK: { +// CHECK: call void @_ZN9TestClass4setIEi(%class.TestClass* %A, i32 %1) +// CHECK: [[WO_CALL:#[0-9]+]] +// CHECK: ret void + +void test1(int a) { + TestClass A; + + A.setI(a); +} + +// CHECK: declare void @_ZN9TestClass4setIEi(%class.TestClass*, i32) [[WO:#[0-9]+]] + +// CHECK: attributes [[WO]] = { optsize writeonly{{.*}} } +// CHECK: attributes [[WO_CALL]] = { optsize writeonly } Index: test/CodeGen/attributes.c =================================================================== --- test/CodeGen/attributes.c +++ test/CodeGen/attributes.c @@ -107,10 +107,27 @@ (*p)(); } +// CHECK: define void @t25() [[WO_NUW:#[0-9]+]] { +void t25() __attribute__((write_only)); +void t25() __attribute__((write_only)) {} + + +// CHECK: define void @t26(i32* writeonly %b) [[NUW]] { +void t26(int *b __attribute__((write_only))); +void t26(int *b __attribute__((write_only))) {} + +//CHECK: declare void @t27(...) [[WO:#[0-9]+]] +void t27() __attribute__((write_only)); + +void t28(); +void t28() {t27();} + // CHECK: attributes [[NUW]] = { noinline nounwind{{.*}} } // CHECK: attributes [[NR]] = { noinline noreturn nounwind{{.*}} } // CHECK: attributes [[COLDDEF]] = { cold {{.*}}} // CHECK: attributes [[COLDDECL]] = { cold {{.*}}} // CHECK: attributes [[NOCF_CHECK_FUNC]] = { nocf_check {{.*}}} +// CHECK: attributes [[WO_NUW]] = { noinline nounwind optnone writeonly{{.*}}} +// CHECK: attributes [[WO]] = { writeonly{{.*}}} // CHECK: attributes [[COLDSITE]] = { cold {{.*}}} // CHECK: attributes [[NOCF_CHECK_CALL]] = { nocf_check } Index: test/CodeGen/function-attributes.c =================================================================== --- test/CodeGen/function-attributes.c +++ test/CodeGen/function-attributes.c @@ -109,11 +109,24 @@ _setjmp(0); } +// CHECK-LABEL: define void @f22() +// CHECK: [[WO:#[0-9]+]] +// CHECK: { +// CHECK: call void @f21() +// CHECK: [[WO_CALL:#[0-9]+]] +// CHECK: ret void +__attribute__ ((write_only)) void f21(void); +__attribute__ ((write_only)) void f22(void) { + f21(); +} + // CHECK: attributes [[NUW]] = { nounwind optsize{{.*}} } // CHECK: attributes [[AI]] = { alwaysinline nounwind optsize{{.*}} } // CHECK: attributes [[NUW_OS_RN]] = { nounwind optsize readnone{{.*}} } // CHECK: attributes [[SR]] = { nounwind optsize{{.*}} "stackrealign"{{.*}} } // CHECK: attributes [[RT]] = { nounwind optsize returns_twice{{.*}} } +// CHECK: attributes [[WO]] = { nounwind optsize writeonly{{.*}} } // CHECK: attributes [[NR]] = { noreturn optsize } // CHECK: attributes [[NUW_RN]] = { nounwind optsize readnone } // CHECK: attributes [[RT_CALL]] = { optsize returns_twice } +// CHECK: attributes [[WO_CALL]] = { optsize writeonly } Index: test/Misc/pragma-attribute-supported-attributes-list.test =================================================================== --- test/Misc/pragma-attribute-supported-attributes-list.test +++ test/Misc/pragma-attribute-supported-attributes-list.test @@ -2,7 +2,7 @@ // The number of supported attributes should never go down! -// CHECK: #pragma clang attribute supports 74 attributes: +// CHECK: #pragma clang attribute supports 75 attributes: // CHECK-NEXT: AMDGPUFlatWorkGroupSize (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumSGPR (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumVGPR (SubjectMatchRule_function) @@ -75,5 +75,6 @@ // CHECK-NEXT: TestTypestate (SubjectMatchRule_function_is_member) // CHECK-NEXT: TrivialABI (SubjectMatchRule_record) // CHECK-NEXT: WarnUnusedResult (SubjectMatchRule_objc_method, SubjectMatchRule_enum, SubjectMatchRule_record, SubjectMatchRule_hasType_functionType) +// CHECK-NEXT: WriteOnly (SubjectMatchRule_function, SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: XRayInstrument (SubjectMatchRule_function, SubjectMatchRule_objc_method) // CHECK-NEXT: XRayLogArgs (SubjectMatchRule_function, SubjectMatchRule_objc_method) Index: test/Sema/attr-write-only.cpp =================================================================== --- test/Sema/attr-write-only.cpp +++ test/Sema/attr-write-only.cpp @@ -0,0 +1,46 @@ +// RUN: %clang_cc1 -std=c++11 %s -verify -fsyntax-only + +int a __attribute__((write_only)); // expected-error {{'write_only' attribute only applies to functions and parameters}} + +__attribute__((write_only)) void t0(void) { +} + +void t1() __attribute__((write_only)); + +void t2() __attribute__((write_only(2))); // expected-error {{'write_only' attribute takes no arguments}} + +typedef void (*t3)(void) __attribute__((write_only)); // expected-error {{'write_only' attribute only applies to functions and parameters}} + +void t4(int *b __attribute__((write_only))); + +void t5(int c __attribute__((write_only))); // expected-warning {{'write_only' attribute only applies to a pointer or reference }} + +typedef void (*t6)(int *d __attribute__((write_only))); + +int *e __attribute__((write_only)); // expected-error {{'write_only' attribute only applies to functions and parameters}} + +void t7(int *g __attribute__((write_only(3)))); // expected-error {{'write_only' attribute takes no arguments}} + +void t8(int& h __attribute__((write_only))); + +void (*func_ptr)(void) __attribute__((write_only)); // expected-error {{'write_only' attribute only applies to functions and parameters}} + +void (*func_ptr2)(double __attribute__((write_only))); // expected-warning {{'write_only' attribute only applies to a pointer or reference }} + +typedef int (*func_ptr3)(int, int); + +void t9(func_ptr3 __attribute__((write_only))); + +class TestClass { + public: + void setI(int j) __attribute__((write_only)); + private: + int I; +}; + +typedef void(TestClass::*member_func_pntr)(int); + +void useSetter(int i, TestClass* pTestClass, + member_func_pntr mfp __attribute__((write_only))) { // expected-warning {{'write_only' attribute only applies to a pointer or reference }} + return (pTestClass->*mfp)(i); +}