This attribute indicates that the inliner should never inline this
function in any situation. This attribute may not be used together
with the ``alwaysinline`` attribute.
+``nomerge``
+ This attribute indicates that calls to this function should never be merged
+ during optimization. For example, it will prevent tail merging otherwise
+ identical code sequences that raise an exception or terminate the program.
+ Tail merging normally reduces the precision of source location information,
+ making stack traces less useful for debugging. This attribute gives the
+ user control over the tradeoff between code size and debug information
+ precision.
``nonlazybind``
This attribute suppresses lazy symbol binding for the function. This
may make calls to the function faster, at the cost of extra program
ATTR_KIND_NOSYNC = 63,
ATTR_KIND_SANITIZE_MEMTAG = 64,
ATTR_KIND_PREALLOCATED = 65,
+ ATTR_KIND_NO_MERGE = 66,
};
enum ComdatSelectionKindCodes {
/// Function is called early and/or often, so lazy binding isn't worthwhile.
def NonLazyBind : EnumAttr<"nonlazybind">;
+/// Disable merging for call sites
+def NoMerge : EnumAttr<"nomerge">;
+
/// Pointer is known to be not null.
def NonNull : EnumAttr<"nonnull">;
addAttribute(AttributeList::FunctionIndex, Attribute::NoDuplicate);
}
+ /// Determine if the call cannot be tail merged.
+ bool cannotMerge() const { return hasFnAttr(Attribute::NoMerge); }
+
/// Determine if the invoke is convergent
bool isConvergent() const { return hasFnAttr(Attribute::Convergent); }
void setConvergent() {
KEYWORD(noinline);
KEYWORD(norecurse);
KEYWORD(nonlazybind);
+ KEYWORD(nomerge);
KEYWORD(nonnull);
KEYWORD(noredzone);
KEYWORD(noreturn);
B.addAttribute(Attribute::NoImplicitFloat); break;
case lltok::kw_noinline: B.addAttribute(Attribute::NoInline); break;
case lltok::kw_nonlazybind: B.addAttribute(Attribute::NonLazyBind); break;
+ case lltok::kw_nomerge: B.addAttribute(Attribute::NoMerge); break;
case lltok::kw_noredzone: B.addAttribute(Attribute::NoRedZone); break;
case lltok::kw_noreturn: B.addAttribute(Attribute::NoReturn); break;
case lltok::kw_nosync: B.addAttribute(Attribute::NoSync); break;
case lltok::kw_noimplicitfloat:
case lltok::kw_noinline:
case lltok::kw_nonlazybind:
+ case lltok::kw_nomerge:
case lltok::kw_noredzone:
case lltok::kw_noreturn:
case lltok::kw_nocf_check:
case lltok::kw_noimplicitfloat:
case lltok::kw_noinline:
case lltok::kw_nonlazybind:
+ case lltok::kw_nomerge:
case lltok::kw_noredzone:
case lltok::kw_noreturn:
case lltok::kw_nocf_check:
kw_noinline,
kw_norecurse,
kw_nonlazybind,
+ kw_nomerge,
kw_nonnull,
kw_noredzone,
kw_noreturn,
return Attribute::NoInline;
case bitc::ATTR_KIND_NO_RECURSE:
return Attribute::NoRecurse;
+ case bitc::ATTR_KIND_NO_MERGE:
+ return Attribute::NoMerge;
case bitc::ATTR_KIND_NON_LAZY_BIND:
return Attribute::NonLazyBind;
case bitc::ATTR_KIND_NON_NULL:
return bitc::ATTR_KIND_NO_INLINE;
case Attribute::NoRecurse:
return bitc::ATTR_KIND_NO_RECURSE;
+ case Attribute::NoMerge:
+ return bitc::ATTR_KIND_NO_MERGE;
case Attribute::NonLazyBind:
return bitc::ATTR_KIND_NON_LAZY_BIND;
case Attribute::NonNull:
return "noinline";
if (hasAttribute(Attribute::NonLazyBind))
return "nonlazybind";
+ if (hasAttribute(Attribute::NoMerge))
+ return "nomerge";
if (hasAttribute(Attribute::NonNull))
return "nonnull";
if (hasAttribute(Attribute::NoRedZone))
/// Return true if this attribute kind only applies to functions.
static bool isFuncOnlyAttr(Attribute::AttrKind Kind) {
switch (Kind) {
+ case Attribute::NoMerge:
case Attribute::NoReturn:
case Attribute::NoSync:
case Attribute::WillReturn:
case Attribute::NoAlias:
case Attribute::NoBuiltin:
case Attribute::NoCapture:
+ case Attribute::NoMerge:
case Attribute::NoReturn:
case Attribute::NoSync:
case Attribute::None:
if (!TTI.isProfitableToHoist(I1) || !TTI.isProfitableToHoist(I2))
return Changed;
+ // If any of the two call sites has nomerge attribute, stop hoisting.
+ if (const auto *CB1 = dyn_cast<CallBase>(I1))
+ if (CB1->cannotMerge())
+ return Changed;
+ if (const auto *CB2 = dyn_cast<CallBase>(I2))
+ if (CB2->cannotMerge())
+ return Changed;
+
if (isa<DbgInfoIntrinsic>(I1) || isa<DbgInfoIntrinsic>(I2)) {
assert (isa<DbgInfoIntrinsic>(I1) && isa<DbgInfoIntrinsic>(I2));
// The debug location is an integral part of a debug info intrinsic
// Conservatively return false if I is an inline-asm instruction. Sinking
// and merging inline-asm instructions can potentially create arguments
// that cannot satisfy the inline-asm constraints.
+ // If the instruction has nomerge attribute, return false.
if (const auto *C = dyn_cast<CallBase>(I))
- if (C->isInlineAsm())
+ if (C->isInlineAsm() || C->cannotMerge())
return false;
// Each instruction must have zero or one use.
--- /dev/null
+; RUN: opt < %s -O1 -S | FileCheck %s
+
+; The attribute nomerge prevents the 3 bar() calls from being sunk/hoisted into
+; one inside a function. Check that there are still 3 tail calls.
+
+; Test case for preventing sinking
+; CHECK-LABEL: define void @sink
+; CHECK: if.then:
+; CHECK-NEXT: tail call void @bar()
+; CHECK: if.then2:
+; CHECK-NEXT: tail call void @bar()
+; CHECK: if.end3:
+; CHECK-NEXT: tail call void @bar()
+define void @sink(i32 %i) {
+entry:
+ switch i32 %i, label %if.end3 [
+ i32 5, label %if.then
+ i32 7, label %if.then2
+ ]
+
+if.then:
+ tail call void @bar() #0
+ br label %if.end3
+
+if.then2:
+ tail call void @bar() #0
+ br label %if.end3
+
+if.end3:
+ tail call void @bar() #0
+ ret void
+}
+
+; Test case for preventing hoisting
+; CHECK-LABEL: define void @hoist
+; CHECK: if.then:
+; CHECK-NEXT: tail call void @bar()
+; CHECK: if.then2:
+; CHECK-NEXT: tail call void @bar()
+; CHECK: if.end:
+; CHECK-NEXT: tail call void @bar()
+define void @hoist(i32 %i) {
+entry:
+ %i.addr = alloca i32, align 4
+ store i32 %i, i32* %i.addr, align 4
+ %0 = load i32, i32* %i.addr, align 4
+ %cmp = icmp eq i32 %0, 5
+ br i1 %cmp, label %if.then, label %if.else
+
+if.then:
+ tail call void @bar() #1
+ unreachable
+
+if.else:
+ %1 = load i32, i32* %i.addr, align 4
+ %cmp1 = icmp eq i32 %i, 7
+ br i1 %cmp1, label %if.then2, label %if.end
+
+if.then2:
+ tail call void @bar() #1
+ unreachable
+
+if.end:
+ tail call void @bar() #1
+ unreachable
+}
+
+declare void @bar()
+
+attributes #0 = { nomerge }
+attributes #1 = { noreturn nomerge }