Index: llvm/lib/Transforms/Utils/InlineFunction.cpp =================================================================== --- llvm/lib/Transforms/Utils/InlineFunction.cpp +++ llvm/lib/Transforms/Utils/InlineFunction.cpp @@ -1743,6 +1743,31 @@ } } +/// Determine if a caller/callee have incompatible vscale_range attributes to +/// prevent inlining that could otherwise lead to invalid results. +static bool haveIncompatibleVScaleRangeAttributes(const Function &Caller, + const Function &Callee) { + const auto CallerRange = Caller.getFnAttribute(Attribute::VScaleRange); + const auto CalleeRange = Callee.getFnAttribute(Attribute::VScaleRange); + + unsigned CallerMinVScale = 0, CallerMaxVScale = 0; + if (CallerRange.isValid()) + std::tie(CallerMinVScale, CallerMaxVScale) = + CallerRange.getVScaleRangeArgs(); + unsigned CalleeMinVScale = 0, CalleeMaxVScale = 0; + if (CalleeRange.isValid()) + std::tie(CalleeMinVScale, CalleeMaxVScale) = + CalleeRange.getVScaleRangeArgs(); + + if (CallerMaxVScale == 0) + CallerMaxVScale = UINT_MAX; + if (CalleeMaxVScale == 0) + CalleeMaxVScale = UINT_MAX; + + 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. @@ -1805,6 +1830,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,159 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt %s -inline -S | FileCheck %s +; RUN: opt %s -inline -pass-remarks=inline -pass-remarks-missed=inline -o /dev/null 2>&1 | FileCheck %s --check-prefix=CHECK-REMARK + +; Callees + +define void @callee_unbounded() vscale_range(0, 0) { +; CHECK-LABEL: @callee_unbounded( +; CHECK-NEXT: ret void +; + ret void +} + +define void @callee_unbounded_min() vscale_range(0, 4) { +; CHECK-LABEL: @callee_unbounded_min( +; CHECK-NEXT: ret void +; + ret void +} + +define void @callee_restricted_min() vscale_range(2, 4) { +; CHECK-LABEL: @callee_restricted_min( +; CHECK-NEXT: ret void +; + ret void +} + +define void @callee_unrestricted_min() vscale_range(1, 4) { +; CHECK-LABEL: @callee_unrestricted_min( +; CHECK-NEXT: ret void +; + ret void +} + +define void @callee_unbounded_max() vscale_range(1, 0) { +; CHECK-LABEL: @callee_unbounded_max( +; CHECK-NEXT: ret void +; + ret void +} + +define void @callee_restricted_max() vscale_range(1, 8) { +; CHECK-LABEL: @callee_restricted_max( +; CHECK-NEXT: ret void +; + ret void +} + +define void @callee_unrestricted_max() vscale_range(1, 16) { +; CHECK-LABEL: @callee_unrestricted_max( +; CHECK-NEXT: ret void +; + ret void +} + +; Callers + +; CHECK-REMARK: remark: {{.*}} 'callee_unbounded' inlined into 'caller_unbounded__callee_unbounded' +define void @caller_unbounded__callee_unbounded() vscale_range(0, 0) { +; CHECK-LABEL: @caller_unbounded__callee_unbounded( +; CHECK-NEXT: ret void +; + call void @callee_unbounded() + ret void +} + +; CHECK-REMARK: remark: {{.*}} 'callee_unbounded_min' inlined into 'caller_restricted_min__callee_unbounded_min' +define void @caller_restricted_min__callee_unbounded_min() vscale_range(2, 4) { +; CHECK-LABEL: @caller_restricted_min__callee_unbounded_min( +; CHECK-NEXT: ret void +; + call void @callee_unbounded_min() + ret void +} + +; CHECK-REMARK: remark: {{.*}} 'callee_restricted_min' is not inlined into 'caller_unbounded_min__callee_restricted_min': incompatible vscale_range attributes +define void @caller_unbounded_min__callee_restricted_min() vscale_range(0, 4) { +; CHECK-LABEL: @caller_unbounded_min__callee_restricted_min( +; CHECK-NEXT: call void @callee_restricted_min() +; CHECK-NEXT: ret void +; + call void @callee_restricted_min() + ret void +} + +; CHECK-REMARK: remark: {{.*}} 'callee_restricted_min' inlined into 'caller_restricted_min__callee_restricted_min' +define void @caller_restricted_min__callee_restricted_min() vscale_range(2, 4) { +; CHECK-LABEL: @caller_restricted_min__callee_restricted_min( +; CHECK-NEXT: ret void +; + call void @callee_restricted_min() + ret void +} + +; CHECK-REMARK: remark: {{.*}} 'callee_restricted_min' is not inlined into 'caller_unrestricted_min__callee_restricted_min': incompatible vscale_range attributes +define void @caller_unrestricted_min__callee_restricted_min() vscale_range(1, 4) { +; CHECK-LABEL: @caller_unrestricted_min__callee_restricted_min( +; CHECK-NEXT: call void @callee_restricted_min() +; CHECK-NEXT: ret void +; + call void @callee_restricted_min() + ret void +} + +; CHECK-REMARK: remark: {{.*}} 'callee_unrestricted_min' inlined into 'caller_restricted_min__callee_unrestricted_min' +define void @caller_restricted_min__callee_unrestricted_min() vscale_range(2, 4) { +; CHECK-LABEL: @caller_restricted_min__callee_unrestricted_min( +; CHECK-NEXT: ret void +; + call void @callee_unrestricted_min() + ret void +} + +; CHECK-REMARK: remark: {{.*}} 'callee_unbounded_max' inlined into 'caller_restricted_max__callee_unbounded_max' +define void @caller_restricted_max__callee_unbounded_max() vscale_range(1, 8) { +; CHECK-LABEL: @caller_restricted_max__callee_unbounded_max( +; CHECK-NEXT: ret void +; + call void @callee_unbounded_max() + ret void +} + +; CHECK-REMARK: remark: {{.*}} 'callee_restricted_max' is not inlined into 'caller_unbounded_max__callee_restricted_max': incompatible vscale_range attributes +define void @caller_unbounded_max__callee_restricted_max() vscale_range(1, 0) { +; CHECK-LABEL: @caller_unbounded_max__callee_restricted_max( +; CHECK-NEXT: call void @callee_restricted_max() +; CHECK-NEXT: ret void +; + call void @callee_restricted_max() + ret void +} + +; CHECK-REMARK: remark: {{.*}} 'callee_restricted_max' inlined into 'caller_restricted_max__callee_restricted_max' +define void @caller_restricted_max__callee_restricted_max() vscale_range(1, 8) { +; CHECK-LABEL: @caller_restricted_max__callee_restricted_max( +; CHECK-NEXT: ret void +; + call void @callee_restricted_max() + ret void +} + +; CHECK-REMARK: remark: {{.*}} 'callee_unrestricted_max' inlined into 'caller_restricted_max__callee_unrestricted_max' +define void @caller_restricted_max__callee_unrestricted_max() vscale_range(1, 8) { +; CHECK-LABEL: @caller_restricted_max__callee_unrestricted_max( +; CHECK-NEXT: ret void +; + call void @callee_unrestricted_max() + ret void +} + +; CHECK-REMARK: remark: {{.*}} 'callee_restricted_max' is not inlined into 'caller_unrestricted_max__callee_restricted_max': incompatible vscale_range attributes +define void @caller_unrestricted_max__callee_restricted_max() vscale_range(1, 16) { +; CHECK-LABEL: @caller_unrestricted_max__callee_restricted_max( +; CHECK-NEXT: call void @callee_restricted_max() +; CHECK-NEXT: ret void +; + call void @callee_restricted_max() + ret void +}