Index: llvm/lib/Transforms/Utils/InlineFunction.cpp =================================================================== --- llvm/lib/Transforms/Utils/InlineFunction.cpp +++ llvm/lib/Transforms/Utils/InlineFunction.cpp @@ -1749,6 +1749,62 @@ } } +/// Determine if a caller/callee have incompatible vscale_range attributes to +/// prevent inlining that could otherwise lead to invalid results. There are +/// several cases to consider: +/// +/// 1. If neither the caller or callee have a vscale_range attribute, allow +/// inlining. +/// 2. If either of the caller or callee have a vscale_range attribute, but +/// not both, don't inline, since it may be unsafe to do so. +/// 3. If the caller and callee vscale_range attributes are identical, allow +/// inlining. +/// 4. If the callee vscale_range is more restrictive than the caller, don't +/// inline, since it's unsafe. +/// 5. If the callee vscale_range is less restrictive than the caller, allow +/// inlining. It may be profitable to do so. +/// 6. If the caller or callee have an unbounded vscale_range, don't inline, +/// unless case 3 applies. +static bool haveIncompatibleVScaleRangeAttributes(const Function &Caller, + const Function &Callee) { + bool CallerHasVScaleRange = Caller.hasFnAttribute(Attribute::VScaleRange); + bool CalleeHasVScaleRange = Callee.hasFnAttribute(Attribute::VScaleRange); + + // If the caller and callee have no vscale_range attribute, allow inlining. + if (!CallerHasVScaleRange && !CalleeHasVScaleRange) + return false; + + // If either of the caller or callee have a vscale_range attribute, but not + // both, don't allow inlining. + if (CallerHasVScaleRange != CalleeHasVScaleRange) + return true; + + unsigned CallerMinVScale, CallerMaxVScale; + std::tie(CallerMinVScale, CallerMaxVScale) = + Caller.getFunction() + .getFnAttribute(Attribute::VScaleRange) + .getVScaleRangeArgs(); + + unsigned CalleeMinVScale, CalleeMaxVScale; + std::tie(CalleeMinVScale, CalleeMaxVScale) = + Callee.getFunction() + .getFnAttribute(Attribute::VScaleRange) + .getVScaleRangeArgs(); + + if ((CallerMinVScale == CalleeMinVScale) && + (CallerMaxVScale == CalleeMaxVScale)) + return false; + + // Don't inline if the caller or callee have an unbounded vscale_range that + // don't match. + if (!CallerMinVScale || !CallerMaxVScale || !CalleeMinVScale || + !CalleeMaxVScale) + return true; + + return (CallerMinVScale < CalleeMinVScale) || + (CallerMaxVScale > CalleeMaxVScale); +} + /// This function inlines the called function into the basic block of the /// caller. This returns false if it is not possible to inline this call. /// The program is still in a well defined state if this occurs though. @@ -1811,6 +1867,9 @@ return InlineResult::failure("incompatible GC"); } + if (haveIncompatibleVScaleRangeAttributes(*Caller, *CalledFunc)) + return InlineResult::failure("incompatible vscale_range attributes"); + // Get the personality function from the callee if it contains a landing pad. Constant *CalledPersonality = CalledFunc->hasPersonalityFn() Index: llvm/test/Transforms/Inline/inline-vscale-range.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/Inline/inline-vscale-range.ll @@ -0,0 +1,152 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -inline -pass-remarks-missed=inline -S < %s 2>&1 | FileCheck %s + +; CHECK: remark: {{.*}} 'callee_vscale_range_2_4' is not inlined into 'caller_no_vscale_range': incompatible vscale_range attributes +; CHECK: remark: {{.*}} 'callee_no_vscale_range' is not inlined into 'caller_vscale_range_callee_no_vscale_range': incompatible vscale_range attributes +; CHECK: remark: {{.*}} 'callee_vscale_range_2_4' is not inlined into 'caller_vscale_range_1_16': incompatible vscale_range attributes +; CHECK: remark: {{.*}} 'callee_vscale_range_2_4' is not inlined into 'caller_unbounded_vscale_min': incompatible vscale_range attributes +; CHECK: remark: {{.*}} 'callee_vscale_range_2_4' is not inlined into 'caller_unbounded_vscale_max': incompatible vscale_range attributes +; CHECK: remark: {{.*}} 'callee_unbounded_vscale_min' is not inlined into 'caller_vscale_range_2_4_callee_unbounded_vscale_min': incompatible vscale_range attributes +; CHECK: remark: {{.*}} 'callee_unbounded_vscale_max' is not inlined into 'caller_vscale_range_2_4_callee_unbounded_vscale_max': incompatible vscale_range attributes + +; 1. If neither the caller or callee have a vscale_range attribute, allow +; inlining. This is covered by existing tests. +; 2. If either of the caller or callee have a vscale_range attribute, but +; not both, don't inline, since it may be unsafe to do so. +; 3. If the caller and callee vscale_range attributes are identical, allow +; inlining. +; 4. If the callee vscale_range is more restrictive than the caller, don't +; inline, since it's unsafe. +; 5. If the callee vscale_range is less restrictive than the caller, allow +; inlining. It may be profitable to do so. +; 6. If the caller or callee have an unbounded vscale_range, don't inline, +; unless case 3 applies. + +define internal void @callee_vscale_range_2_4() vscale_range(2, 4) { +; CHECK-LABEL: @callee_vscale_range_2_4( +; CHECK-NEXT: ret void +; + ret void +} + +; If the caller or callee have a vscale_range but not both, don't inline. +define void @caller_no_vscale_range() { +; CHECK-LABEL: @caller_no_vscale_range( +; CHECK-NEXT: call void @callee_vscale_range_2_4() +; CHECK-NEXT: ret void +; + call void @callee_vscale_range_2_4() + ret void +} + +define internal void @callee_no_vscale_range() { +; CHECK-LABEL: @callee_no_vscale_range( +; CHECK-NEXT: ret void +; + ret void +} + +define void @caller_vscale_range_callee_no_vscale_range() vscale_range(1, 16) { +; CHECK-LABEL: @caller_vscale_range_callee_no_vscale_range( +; CHECK-NEXT: call void @callee_no_vscale_range() +; CHECK-NEXT: ret void +; + call void @callee_no_vscale_range() + ret void +} + +; If the callee's vscale_range is identical to the caller, inline. +define void @caller_vscale_range_2_4() vscale_range(2, 4) { +; CHECK-LABEL: @caller_vscale_range_2_4( +; CHECK-NEXT: ret void +; + call void @callee_vscale_range_2_4() + ret void +} + +define internal void @callee_vscale_range_0_16() vscale_range(0, 16) { + ret void +} + +; Even if it's unbounded. +define void @caller_vscale_range_0_16() vscale_range(0, 16) { +; CHECK-LABEL: @caller_vscale_range_0_16( +; CHECK-NEXT: ret void +; + call void @callee_vscale_range_0_16() + ret void +} + +; If the callee's vscale_range is more restrictive than the caller, don't +; inline. +define void @caller_vscale_range_1_16() vscale_range(1, 16) { +; CHECK-LABEL: @caller_vscale_range_1_16( +; CHECK-NEXT: call void @callee_vscale_range_2_4() +; CHECK-NEXT: ret void +; + call void @callee_vscale_range_2_4() + ret void +} + +define void @caller_unbounded_vscale_min() vscale_range(0, 4) { +; CHECK-LABEL: @caller_unbounded_vscale_min( +; CHECK-NEXT: call void @callee_vscale_range_2_4() +; CHECK-NEXT: ret void +; + call void @callee_vscale_range_2_4() + ret void +} + +define void @caller_unbounded_vscale_max() vscale_range(2, 0) { +; CHECK-LABEL: @caller_unbounded_vscale_max( +; CHECK-NEXT: call void @callee_vscale_range_2_4() +; CHECK-NEXT: ret void +; + call void @callee_vscale_range_2_4() + ret void +} + +; If the callee vscale_range is less restrictive than caller, inline. +define internal void @callee_vscale_range_1_16() vscale_range(1, 16) { + ret void +} + +define void @caller_vscale_range_2_4_callee_vscale_range_1_16() vscale_range(2, 4) { +; CHECK-LABEL: @caller_vscale_range_2_4_callee_vscale_range_1_16( +; CHECK-NEXT: ret void +; + call void @callee_vscale_range_1_16() + ret void +} + +define internal void @callee_unbounded_vscale_min() vscale_range(0, 4) { +; CHECK-LABEL: @callee_unbounded_vscale_min( +; CHECK-NEXT: ret void +; + ret void +} + +define internal void @callee_unbounded_vscale_max() vscale_range(2, 0) { +; CHECK-LABEL: @callee_unbounded_vscale_max( +; CHECK-NEXT: ret void +; + ret void +} + +define void @caller_vscale_range_2_4_callee_unbounded_vscale_min() vscale_range(2, 4) { +; CHECK-LABEL: @caller_vscale_range_2_4_callee_unbounded_vscale_min( +; CHECK-NEXT: call void @callee_unbounded_vscale_min() +; CHECK-NEXT: ret void +; + call void @callee_unbounded_vscale_min() + ret void +} + +define void @caller_vscale_range_2_4_callee_unbounded_vscale_max() vscale_range(2, 4) { +; CHECK-LABEL: @caller_vscale_range_2_4_callee_unbounded_vscale_max( +; CHECK-NEXT: call void @callee_unbounded_vscale_max() +; CHECK-NEXT: ret void +; + call void @callee_unbounded_vscale_max() + ret void +}