diff --git a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp --- a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp +++ b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp @@ -312,6 +312,12 @@ AAMDNodes AAInfo; OrigLoad->getAAMetadata(AAInfo); newLoad->setAAMetadata(AAInfo); + // And other metadata. + newLoad->copyMetadata( + *OrigLoad, + {LLVMContext::MD_nontemporal, LLVMContext::MD_nonnull, + LLVMContext::MD_dereferenceable, LLVMContext::MD_align, + LLVMContext::MD_noundef, LLVMContext::MD_range}); Args.push_back(newLoad); ArgAttrVec.push_back(AttributeSet()); diff --git a/llvm/test/Transforms/ArgumentPromotion/metadata.ll b/llvm/test/Transforms/ArgumentPromotion/metadata.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/ArgumentPromotion/metadata.ll @@ -0,0 +1,70 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes +; RUN: opt < %s -argpromotion -S | FileCheck %s + +define i32 @should_copy_range(i32* %x) { +; CHECK-LABEL: define {{[^@]+}}@should_copy_range +; CHECK-SAME: (i32* [[X:%.*]]) { +; CHECK-NEXT: [[X_VAL:%.*]] = load i32, i32* [[X]], align 4, !range !0 +; CHECK-NEXT: [[TMP1:%.*]] = call i32 @f_load_range(i32 [[X_VAL]]) +; CHECK-NEXT: ret i32 [[TMP1]] +; + %1 = call i32 @f_load_range(i32* %x) + ret i32 %1 +} + +define internal i32 @f_load_range(i32* %v) { +; CHECK-LABEL: define {{[^@]+}}@f_load_range +; CHECK-SAME: (i32 [[V_VAL:%.*]]) { +; CHECK-NEXT: ret i32 [[V_VAL]] +; + %1 = load i32, i32* %v, align 4, !range !0 + ret i32 %1 +} + +define i32* @should_copy_nonnull(i32** %x) { +; CHECK-LABEL: define {{[^@]+}}@should_copy_nonnull +; CHECK-SAME: (i32** [[X:%.*]]) { +; CHECK-NEXT: [[X_VAL:%.*]] = load i32*, i32** [[X]], align 4, !nonnull !1 +; CHECK-NEXT: [[TMP1:%.*]] = call i32* @f_load_nonnull(i32* [[X_VAL]]) +; CHECK-NEXT: ret i32* [[TMP1]] +; + %1 = call i32* @f_load_nonnull(i32** %x) + ret i32* %1 +} + +define internal i32* @f_load_nonnull(i32** %v) { +; CHECK-LABEL: define {{[^@]+}}@f_load_nonnull +; CHECK-SAME: (i32* [[V_VAL:%.*]]) { +; CHECK-NEXT: ret i32* [[V_VAL]] +; + %1 = load i32*, i32** %v, align 4, !nonnull !1 + ret i32* %1 +} + +define i32* @should_copy_dereferenceable(i32** %x) { +; CHECK-LABEL: define {{[^@]+}}@should_copy_dereferenceable +; CHECK-SAME: (i32** [[X:%.*]]) { +; CHECK-NEXT: [[X_VAL:%.*]] = load i32*, i32** [[X]], align 4, !dereferenceable !2 +; CHECK-NEXT: [[TMP1:%.*]] = call i32* @f_load_dereferenceable(i32* [[X_VAL]]) +; CHECK-NEXT: ret i32* [[TMP1]] +; + %1 = call i32* @f_load_dereferenceable(i32** %x) + ret i32* %1 +} + +define internal i32* @f_load_dereferenceable(i32** %v) { +; CHECK-LABEL: define {{[^@]+}}@f_load_dereferenceable +; CHECK-SAME: (i32* [[V_VAL:%.*]]) { +; CHECK-NEXT: ret i32* [[V_VAL]] +; + %1 = load i32*, i32** %v, align 4, !dereferenceable !2 + ret i32* %1 +} + +; CHECK: !0 = !{i32 0, i32 4} +; CHECK-NEXT: !1 = !{} +; CHECK-NEXT: !2 = !{i64 42} +; +!0 = !{i32 0, i32 4} +!1 = !{} +!2 = !{i64 42}