Index: lib/Transforms/IPO/FunctionAttrs.cpp =================================================================== --- lib/Transforms/IPO/FunctionAttrs.cpp +++ lib/Transforms/IPO/FunctionAttrs.cpp @@ -74,6 +74,7 @@ STATISTIC(NumNonNullReturn, "Number of function returns marked nonnull"); STATISTIC(NumNoRecurse, "Number of functions marked as norecurse"); STATISTIC(NumNoUnwind, "Number of functions marked as nounwind"); +STATISTIC(NumSpeculatable, "Number of functions marked as speculatable"); // FIXME: This is disabled by default to avoid exposing security vulnerabilities // in C/C++ code compiled by clang: @@ -87,6 +88,10 @@ "disable-nounwind-inference", cl::Hidden, cl::desc("Stop inferring nounwind attribute during function-attrs pass")); +static cl::opt DisableSpeculatableInference( + "disable-speculatable-inference", cl::Hidden, + cl::desc("Stop inferring speculatable attribute during function-attrs pass")); + namespace { using SCCNodeSet = SmallSetVector; @@ -1193,6 +1198,25 @@ return true; } +/// Helper for Speculatable inference predicate InstrBreaksAttribute. +static bool InstrBreaksSpeculatable(Instruction &I, const SCCNodeSet &SCCNodes) { + // Ignore functions within our own SCC (we're doing optimistic inference). + if (const auto *CI = dyn_cast(&I)) + if (Function *Callee = CI->getCalledFunction()) + if (SCCNodes.count(Callee) > 0) + return false; + + // Note that we need to assume here that any inputs to the function might be + // poison, and in response, we can generate more poison, but not undefined + // behavior. This means no branching (and essentially no stores). + + // Returns are okay in this context. + if (isa(I)) + return false; + + return !isSafeToSpeculativelyExecute(&I); +} + /// Infer attributes from all functions in the SCC by scanning every /// instruction for compliance to the attribute assumptions. Currently it /// does: @@ -1246,6 +1270,28 @@ }, /* RequiresExactDefinition= */ true}); + if (!DisableSpeculatableInference) + // Request to infer speculatable attribute for all the functions in the SCC if + // every instruction within the SCC is safe to speculatively execute (except + // for calls to functions within the SCC). Note that the speculatable attribute + // suffers from derefinement - results may change depending on how functions are + // optimized. Thus it can be inferred only from exact definitions. + AI.registerAttrInference(AttributeInferer::InferenceDescriptor{ + Attribute::Speculatable, + // Skip speculatable functions. + [](const Function &F) { return F.isSpeculatable(); }, + // Instructions that break non-throwing assumption. + [SCCNodes](Instruction &I) { + return InstrBreaksSpeculatable(I, SCCNodes); + }, + [](Function &F) { + LLVM_DEBUG(dbgs() + << "Adding speculatable attr to fn " << F.getName() << "\n"); + F.setSpeculatable(); + ++NumSpeculatable; + }, + /* RequiresExactDefinition= */ true}); + // Perform all the requested attribute inference actions. return AI.run(SCCNodes); } Index: test/CodeGen/AMDGPU/inline-attr.ll =================================================================== --- test/CodeGen/AMDGPU/inline-attr.ll +++ test/CodeGen/AMDGPU/inline-attr.ll @@ -6,13 +6,13 @@ ; GCN: define amdgpu_kernel void @caller(float addrspace(1)* nocapture %p) local_unnamed_addr #1 { ; GCN: %mul.i = fmul float %load, 1.500000e+01 -; UNSAFE: attributes #0 = { norecurse nounwind readnone "less-precise-fpmad"="true" "no-infs-fp-math"="true" "no-nans-fp-math"="true" "unsafe-fp-math"="true" } +; UNSAFE: attributes #0 = { norecurse nounwind readnone speculatable "less-precise-fpmad"="true" "no-infs-fp-math"="true" "no-nans-fp-math"="true" "unsafe-fp-math"="true" } ; UNSAFE: attributes #1 = { norecurse nounwind "less-precise-fpmad"="true" "no-infs-fp-math"="true" "no-nans-fp-math"="true" "unsafe-fp-math"="true" } -; NOINFS: attributes #0 = { norecurse nounwind readnone "no-infs-fp-math"="true" } +; NOINFS: attributes #0 = { norecurse nounwind readnone speculatable "no-infs-fp-math"="true" } ; NOINFS: attributes #1 = { norecurse nounwind "less-precise-fpmad"="false" "no-infs-fp-math"="true" "no-nans-fp-math"="false" "unsafe-fp-math"="false" } -; NONANS: attributes #0 = { norecurse nounwind readnone "no-nans-fp-math"="true" } +; NONANS: attributes #0 = { norecurse nounwind readnone speculatable "no-nans-fp-math"="true" } ; NONANS: attributes #1 = { norecurse nounwind "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="true" "unsafe-fp-math"="false" } define float @foo(float %x) #0 { Index: test/Transforms/FunctionAttrs/operand-bundles-scc.ll =================================================================== --- test/Transforms/FunctionAttrs/operand-bundles-scc.ll +++ test/Transforms/FunctionAttrs/operand-bundles-scc.ll @@ -14,4 +14,4 @@ } -; CHECK: attributes #0 = { nounwind } +; CHECK: attributes #0 = { nounwind speculatable } Index: test/Transforms/FunctionAttrs/optnone.ll =================================================================== --- test/Transforms/FunctionAttrs/optnone.ll +++ test/Transforms/FunctionAttrs/optnone.ll @@ -20,6 +20,6 @@ ; CHECK: (i8*) #1 ; CHECK-LABEL: attributes #0 -; CHECK: = { norecurse nounwind readnone } +; CHECK: = { norecurse nounwind readnone speculatable } ; CHECK-LABEL: attributes #1 ; CHECK: = { noinline optnone } Index: test/Transforms/Reassociate/reassociate-deadinst.ll =================================================================== --- test/Transforms/Reassociate/reassociate-deadinst.ll +++ test/Transforms/Reassociate/reassociate-deadinst.ll @@ -1,4 +1,7 @@ -; RUN: opt < %s -inline -functionattrs -reassociate -S | FileCheck %s +; RUN: opt < %s -inline -functionattrs -reassociate -disable-speculatable-inference -S | FileCheck %s +; Note: We disable inference of the speculatable attribute here so that the +; call to @func1 ends up in the ValueRankMap (requiring mayBeMemoryDependent to +; return true) so that the call will be removed after its uses are removed. ; CHECK-NOT: func1 ; CHECK-LABEL: main