Index: docs/LangRef.rst =================================================================== --- docs/LangRef.rst +++ docs/LangRef.rst @@ -1381,6 +1381,9 @@ This function attribute indicates that the function does not call itself either directly or indirectly down any possible call path. This produces undefined behavior at runtime if the function ever does recurse. +``nosideffects`` + This function attribute indicates that the function has no side effects + and can be safely speculated. ``nounwind`` This function attribute indicates that the function never raises an exception. If the function does raise an exception, its runtime Index: include/llvm/Bitcode/LLVMBitCodes.h =================================================================== --- include/llvm/Bitcode/LLVMBitCodes.h +++ include/llvm/Bitcode/LLVMBitCodes.h @@ -520,7 +520,8 @@ ATTR_KIND_NO_RECURSE = 48, ATTR_KIND_INACCESSIBLEMEM_ONLY = 49, ATTR_KIND_INACCESSIBLEMEM_OR_ARGMEMONLY = 50, - ATTR_KIND_ALLOC_SIZE = 51 + ATTR_KIND_ALLOC_SIZE = 51, + ATTR_KIND_NO_SIDEEFFECTS = 52 }; enum ComdatSelectionKindCodes { Index: include/llvm/IR/Attributes.td =================================================================== --- include/llvm/IR/Attributes.td +++ include/llvm/IR/Attributes.td @@ -106,6 +106,9 @@ /// Mark the function as not returning. def NoReturn : EnumAttr<"noreturn">; +/// Function doesn't have sideeffects. +def NoSideEffects : EnumAttr<"nosideeffects">; + /// Function doesn't unwind stack. def NoUnwind : EnumAttr<"nounwind">; Index: include/llvm/IR/Function.h =================================================================== --- include/llvm/IR/Function.h +++ include/llvm/IR/Function.h @@ -351,6 +351,14 @@ removeFnAttr(Attribute::Convergent); } + /// @brief Determine if the call has sideeffects. + bool doesNotHaveSideEffects() const { + return hasFnAttribute(Attribute::NoSideEffects); + } + void setDoesNotHaveSideEffects() { + addFnAttr(Attribute::NoSideEffects); + } + /// Determine if the function is known not to recurse, directly or /// indirectly. bool doesNotRecurse() const { Index: include/llvm/IR/Intrinsics.td =================================================================== --- include/llvm/IR/Intrinsics.td +++ include/llvm/IR/Intrinsics.td @@ -23,8 +23,8 @@ // is assumed (it may read and write any memory it can get access to and it may // have other side effects). -// IntrNoMem - The intrinsic does not access memory or have any other side -// effects. It may be CSE'd deleted if dead, etc. +// IntrNoMem - The intrinsic does not access memory. However, it may have side +// effects. def IntrNoMem : IntrinsicProperty; // IntrReadMem - This intrinsic only reads from memory. It does not write to @@ -77,6 +77,9 @@ // Parallels the convergent attribute on LLVM IR functions. def IntrConvergent : IntrinsicProperty; +// Calls to this intrinsics have no side effects, so it may be speculated. +def IntrNoSideEffects : IntrinsicProperty; + //===----------------------------------------------------------------------===// // Types used by intrinsics. //===----------------------------------------------------------------------===// Index: lib/AsmParser/LLLexer.cpp =================================================================== --- lib/AsmParser/LLLexer.cpp +++ lib/AsmParser/LLLexer.cpp @@ -639,6 +639,7 @@ KEYWORD(nonnull); KEYWORD(noredzone); KEYWORD(noreturn); + KEYWORD(nosideeffects); KEYWORD(nounwind); KEYWORD(optnone); KEYWORD(optsize); Index: lib/AsmParser/LLParser.cpp =================================================================== --- lib/AsmParser/LLParser.cpp +++ lib/AsmParser/LLParser.cpp @@ -1083,6 +1083,7 @@ case lltok::kw_noredzone: B.addAttribute(Attribute::NoRedZone); break; case lltok::kw_noreturn: B.addAttribute(Attribute::NoReturn); break; case lltok::kw_norecurse: B.addAttribute(Attribute::NoRecurse); break; + case lltok::kw_nosideeffects: B.addAttribute(Attribute::NoSideEffects); break; case lltok::kw_nounwind: B.addAttribute(Attribute::NoUnwind); break; case lltok::kw_optnone: B.addAttribute(Attribute::OptimizeNone); break; case lltok::kw_optsize: B.addAttribute(Attribute::OptimizeForSize); break; Index: lib/AsmParser/LLToken.h =================================================================== --- lib/AsmParser/LLToken.h +++ lib/AsmParser/LLToken.h @@ -144,6 +144,7 @@ kw_nonnull, kw_noredzone, kw_noreturn, + kw_nosideeffects, kw_nounwind, kw_optnone, kw_optsize, Index: lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- lib/Bitcode/Reader/BitcodeReader.cpp +++ lib/Bitcode/Reader/BitcodeReader.cpp @@ -1427,6 +1427,8 @@ return Attribute::NoRedZone; case bitc::ATTR_KIND_NO_RETURN: return Attribute::NoReturn; + case bitc::ATTR_KIND_NO_SIDEEFFECTS: + return Attribute::NoSideEffects; case bitc::ATTR_KIND_NO_UNWIND: return Attribute::NoUnwind; case bitc::ATTR_KIND_OPTIMIZE_FOR_SIZE: Index: lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- lib/Bitcode/Writer/BitcodeWriter.cpp +++ lib/Bitcode/Writer/BitcodeWriter.cpp @@ -593,6 +593,8 @@ return bitc::ATTR_KIND_NO_INLINE; case Attribute::NoRecurse: return bitc::ATTR_KIND_NO_RECURSE; + case Attribute::NoSideEffects: + return bitc::ATTR_KIND_NO_SIDEEFFECTS; case Attribute::NonLazyBind: return bitc::ATTR_KIND_NON_LAZY_BIND; case Attribute::NonNull: Index: lib/IR/Attributes.cpp =================================================================== --- lib/IR/Attributes.cpp +++ lib/IR/Attributes.cpp @@ -282,6 +282,8 @@ return "noreturn"; if (hasAttribute(Attribute::NoRecurse)) return "norecurse"; + if (hasAttribute(Attribute::NoSideEffects)) + return "nosideeffects"; if (hasAttribute(Attribute::NoUnwind)) return "nounwind"; if (hasAttribute(Attribute::OptimizeNone)) @@ -516,6 +518,7 @@ case Attribute::InaccessibleMemOrArgMemOnly: return 1ULL << 50; case Attribute::SwiftSelf: return 1ULL << 51; case Attribute::SwiftError: return 1ULL << 52; + case Attribute::NoSideEffects: return 1ULL << 53; case Attribute::Dereferenceable: llvm_unreachable("dereferenceable attribute not supported in raw format"); break; Index: lib/IR/Verifier.cpp =================================================================== --- lib/IR/Verifier.cpp +++ lib/IR/Verifier.cpp @@ -1280,6 +1280,7 @@ I->getKindAsEnum() == Attribute::NoRecurse || I->getKindAsEnum() == Attribute::InaccessibleMemOnly || I->getKindAsEnum() == Attribute::InaccessibleMemOrArgMemOnly || + I->getKindAsEnum() == Attribute::NoSideEffects || I->getKindAsEnum() == Attribute::AllocSize) { if (!isFunction) { CheckFailed("Attribute '" + I->getAsString() + Index: test/Bindings/llvm-c/invalid-bitcode.test =================================================================== --- test/Bindings/llvm-c/invalid-bitcode.test +++ test/Bindings/llvm-c/invalid-bitcode.test @@ -1,13 +1,13 @@ ; RUN: not llvm-c-test --module-dump < %S/Inputs/invalid.ll.bc 2>&1 | FileCheck %s ; RUN: not llvm-c-test --lazy-module-dump < %S/Inputs/invalid.ll.bc 2>&1 | FileCheck %s -CHECK: Error parsing bitcode: Unknown attribute kind (52) +CHECK: Error parsing bitcode: Unknown attribute kind (53) ; RUN: not llvm-c-test --new-module-dump < %S/Inputs/invalid.ll.bc 2>&1 | FileCheck --check-prefix=NEW %s ; RUN: not llvm-c-test --lazy-new-module-dump < %S/Inputs/invalid.ll.bc 2>&1 | FileCheck --check-prefix=NEW %s -NEW: Error with new bitcode parser: Unknown attribute kind (52) +NEW: Error with new bitcode parser: Unknown attribute kind (53) ; RUN: llvm-c-test --test-diagnostic-handler < %S/Inputs/invalid.ll.bc 2>&1 | FileCheck --check-prefix=DIAGNOSTIC %s Index: test/Bitcode/attributes.ll =================================================================== --- test/Bitcode/attributes.ll +++ test/Bitcode/attributes.ll @@ -204,7 +204,7 @@ ; CHECK: define void @f34() { call void @nobuiltin() nobuiltin -; CHECK: call void @nobuiltin() #32 +; CHECK: call void @nobuiltin() #33 ret void; } @@ -328,6 +328,11 @@ ret i8* null } +; CHECK: define void @f56() #32 +define void @f56() nosideeffects { + ret void +} + ; CHECK: attributes #0 = { noreturn } ; CHECK: attributes #1 = { nounwind } ; CHECK: attributes #2 = { readnone } @@ -360,4 +365,5 @@ ; CHECK: attributes #29 = { inaccessiblemem_or_argmemonly } ; CHECK: attributes #30 = { allocsize(0) } ; CHECK: attributes #31 = { allocsize(0,1) } -; CHECK: attributes #32 = { nobuiltin } +; CHECK: attributes #32 = { nosideeffects } +; CHECK: attributes #33 = { nobuiltin } Index: test/Bitcode/invalid.ll =================================================================== --- test/Bitcode/invalid.ll +++ test/Bitcode/invalid.ll @@ -1,6 +1,6 @@ ; RUN: not llvm-dis < %s.bc 2>&1 | FileCheck %s -; CHECK: llvm-dis{{(\.EXE|\.exe)?}}: error: Unknown attribute kind (52) +; CHECK: llvm-dis{{(\.EXE|\.exe)?}}: error: Unknown attribute kind (53) ; invalid.ll.bc has an invalid attribute number. ; The test checks that LLVM reports the error and doesn't access freed memory Index: test/LTO/X86/invalid.ll =================================================================== --- test/LTO/X86/invalid.ll +++ test/LTO/X86/invalid.ll @@ -1,4 +1,4 @@ ; RUN: not llvm-lto %S/Inputs/invalid.ll.bc 2>&1 | FileCheck %s -; CHECK: llvm-lto{{.*}}: error loading file '{{.*}}/Inputs/invalid.ll.bc': Unknown attribute kind (52) +; CHECK: llvm-lto{{.*}}: error loading file '{{.*}}/Inputs/invalid.ll.bc': Unknown attribute kind (53) Index: utils/TableGen/CodeGenInstruction.h =================================================================== --- utils/TableGen/CodeGenInstruction.h +++ utils/TableGen/CodeGenInstruction.h @@ -257,6 +257,7 @@ bool isInsertSubreg : 1; bool isConvergent : 1; bool hasNoSchedulingInfo : 1; + bool isNoSideEffects : 1; std::string DeprecatedReason; bool HasComplexDeprecationPredicate; Index: utils/TableGen/CodeGenIntrinsics.h =================================================================== --- utils/TableGen/CodeGenIntrinsics.h +++ utils/TableGen/CodeGenIntrinsics.h @@ -109,6 +109,9 @@ /// isConvergent - True if the intrinsic is marked as convergent. bool isConvergent; + /// isNoSideEffects - True if the intrinsic is marked as nosideeffects + bool isNoSideEffects; + enum ArgAttribute { NoCapture, ReadOnly, Index: utils/TableGen/CodeGenTarget.cpp =================================================================== --- utils/TableGen/CodeGenTarget.cpp +++ utils/TableGen/CodeGenTarget.cpp @@ -455,6 +455,7 @@ isNoReturn = false; isNoDuplicate = false; isConvergent = false; + isNoSideEffects = false; if (DefName.size() <= 4 || std::string(DefName.begin(), DefName.begin() + 4) != "int_") @@ -589,6 +590,8 @@ isConvergent = true; else if (Property->getName() == "IntrNoReturn") isNoReturn = true; + else if (Property->getName() == "IntrNoSideEffects") + isNoSideEffects = true; else if (Property->isSubClassOf("NoCapture")) { unsigned ArgNo = Property->getValueAsInt("ArgNo"); ArgumentAttributes.push_back(std::make_pair(ArgNo, NoCapture)); Index: utils/TableGen/IntrinsicEmitter.cpp =================================================================== --- utils/TableGen/IntrinsicEmitter.cpp +++ utils/TableGen/IntrinsicEmitter.cpp @@ -461,6 +461,9 @@ if (L->isConvergent != R->isConvergent) return R->isConvergent; + if (L->isNoSideEffects != R->isNoSideEffects) + return R->isNoSideEffects; + // Try to order by readonly/readnone attribute. CodeGenIntrinsic::ModRefBehavior LK = L->ModRef; CodeGenIntrinsic::ModRefBehavior RK = R->ModRef; @@ -573,7 +576,7 @@ if (!intrinsic.canThrow || intrinsic.ModRef != CodeGenIntrinsic::ReadWriteMem || intrinsic.isNoReturn || intrinsic.isNoDuplicate || - intrinsic.isConvergent) { + intrinsic.isConvergent || intrinsic.isNoSideEffects) { OS << " const Attribute::AttrKind Atts[] = {"; bool addComma = false; if (!intrinsic.canThrow) { @@ -598,6 +601,12 @@ OS << "Attribute::Convergent"; addComma = true; } + if (intrinsic.isNoSideEffects) { + if (addComma) + OS << ","; + OS << "Attribute::NoSideEffects"; + addComma = true; + } switch (intrinsic.ModRef) { case CodeGenIntrinsic::NoMem: