Per LangRef volatile operations can read and write inaccessible memory: > any volatile operation can read and/or modify state which is not > accessible via a regular load or store in this module Model this by adding inaccessible memory effects in getMemoryEffects() if the operation is volatile. In the future, we should model volatile using operand bundles instead. Fixes https://github.com/llvm/llvm-project/issues/120932.
652 lines
24 KiB
LLVM
652 lines
24 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --version 4
|
|
; RUN: opt -passes=function-attrs -S < %s | FileCheck %s
|
|
|
|
define void @basic(ptr %p) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
|
|
; CHECK-LABEL: define void @basic(
|
|
; CHECK-SAME: ptr writeonly captures(none) initializes((0, 8)) [[P:%.*]]) #[[ATTR0:[0-9]+]] {
|
|
; CHECK-NEXT: store i64 123, ptr [[P]], align 4
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
store i64 123, ptr %p
|
|
ret void
|
|
}
|
|
|
|
define void @stores_on_both_paths(ptr %p, i1 %i) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
|
|
; CHECK-LABEL: define void @stores_on_both_paths(
|
|
; CHECK-SAME: ptr writeonly captures(none) initializes((0, 8)) [[P:%.*]], i1 [[I:%.*]]) #[[ATTR0]] {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 [[I]], label [[BB1:%.*]], label [[BB2:%.*]]
|
|
; CHECK: bb1:
|
|
; CHECK-NEXT: store i64 123, ptr [[P]], align 4
|
|
; CHECK-NEXT: br label [[END:%.*]]
|
|
; CHECK: bb2:
|
|
; CHECK-NEXT: store i64 321, ptr [[P]], align 4
|
|
; CHECK-NEXT: br label [[END]]
|
|
; CHECK: end:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
br i1 %i, label %bb1, label %bb2
|
|
bb1:
|
|
store i64 123, ptr %p
|
|
br label %end
|
|
bb2:
|
|
store i64 321, ptr %p
|
|
br label %end
|
|
end:
|
|
ret void
|
|
}
|
|
|
|
define void @store_pointer_to_pointer(ptr %p, ptr %p2) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
|
|
; CHECK-LABEL: define void @store_pointer_to_pointer(
|
|
; CHECK-SAME: ptr [[P:%.*]], ptr writeonly captures(none) initializes((0, 8)) [[P2:%.*]]) #[[ATTR0]] {
|
|
; CHECK-NEXT: store ptr [[P]], ptr [[P2]], align 8
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
store ptr %p, ptr %p2
|
|
ret void
|
|
}
|
|
|
|
; TODO: this is still initializes
|
|
define void @store_pointer_to_itself(ptr %p) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
|
|
; CHECK-LABEL: define void @store_pointer_to_itself(
|
|
; CHECK-SAME: ptr [[P:%.*]]) #[[ATTR0]] {
|
|
; CHECK-NEXT: store ptr [[P]], ptr [[P]], align 8
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
store ptr %p, ptr %p
|
|
ret void
|
|
}
|
|
|
|
define void @load_before_store(ptr %p) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
|
|
; CHECK-LABEL: define void @load_before_store(
|
|
; CHECK-SAME: ptr captures(none) [[P:%.*]]) #[[ATTR1:[0-9]+]] {
|
|
; CHECK-NEXT: [[A:%.*]] = load i32, ptr [[P]], align 4
|
|
; CHECK-NEXT: store i32 123, ptr [[P]], align 4
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%a = load i32, ptr %p
|
|
store i32 123, ptr %p
|
|
ret void
|
|
}
|
|
|
|
define void @partial_load_before_store(ptr %p) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
|
|
; CHECK-LABEL: define void @partial_load_before_store(
|
|
; CHECK-SAME: ptr captures(none) initializes((4, 8)) [[P:%.*]]) #[[ATTR1]] {
|
|
; CHECK-NEXT: [[A:%.*]] = load i32, ptr [[P]], align 4
|
|
; CHECK-NEXT: store i64 123, ptr [[P]], align 4
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%a = load i32, ptr %p
|
|
store i64 123, ptr %p
|
|
ret void
|
|
}
|
|
|
|
declare void @use(ptr)
|
|
|
|
define void @call_clobber(ptr %p) {
|
|
; CHECK-LABEL: define void @call_clobber(
|
|
; CHECK-SAME: ptr [[P:%.*]]) {
|
|
; CHECK-NEXT: call void @use(ptr [[P]])
|
|
; CHECK-NEXT: store i64 123, ptr [[P]], align 4
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
call void @use(ptr %p)
|
|
store i64 123, ptr %p
|
|
ret void
|
|
}
|
|
|
|
define void @call_clobber_after_store(ptr %p) {
|
|
; CHECK-LABEL: define void @call_clobber_after_store(
|
|
; CHECK-SAME: ptr initializes((0, 8)) [[P:%.*]]) {
|
|
; CHECK-NEXT: store i64 123, ptr [[P]], align 4
|
|
; CHECK-NEXT: call void @use(ptr [[P]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
store i64 123, ptr %p
|
|
call void @use(ptr %p)
|
|
ret void
|
|
}
|
|
|
|
define void @store_offset(ptr %p) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
|
|
; CHECK-LABEL: define void @store_offset(
|
|
; CHECK-SAME: ptr writeonly captures(none) initializes((8, 12)) [[P:%.*]]) #[[ATTR0]] {
|
|
; CHECK-NEXT: [[G:%.*]] = getelementptr i8, ptr [[P]], i64 8
|
|
; CHECK-NEXT: store i32 123, ptr [[G]], align 4
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%g = getelementptr i8, ptr %p, i64 8
|
|
store i32 123, ptr %g
|
|
ret void
|
|
}
|
|
|
|
define void @store_volatile(ptr %p) {
|
|
; CHECK: Function Attrs: nofree norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite)
|
|
; CHECK-LABEL: define void @store_volatile(
|
|
; CHECK-SAME: ptr [[P:%.*]]) #[[ATTR2:[0-9]+]] {
|
|
; CHECK-NEXT: [[G:%.*]] = getelementptr i8, ptr [[P]], i64 8
|
|
; CHECK-NEXT: store volatile i32 123, ptr [[G]], align 4
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%g = getelementptr i8, ptr %p, i64 8
|
|
store volatile i32 123, ptr %g
|
|
ret void
|
|
}
|
|
|
|
define void @merge_store_ranges(ptr %p) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
|
|
; CHECK-LABEL: define void @merge_store_ranges(
|
|
; CHECK-SAME: ptr writeonly captures(none) initializes((0, 8)) [[P:%.*]]) #[[ATTR0]] {
|
|
; CHECK-NEXT: [[G:%.*]] = getelementptr i8, ptr [[P]], i64 4
|
|
; CHECK-NEXT: store i32 123, ptr [[G]], align 4
|
|
; CHECK-NEXT: store i32 123, ptr [[P]], align 4
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%g = getelementptr i8, ptr %p, i64 4
|
|
store i32 123, ptr %g
|
|
store i32 123, ptr %p
|
|
ret void
|
|
}
|
|
|
|
define void @partially_overlapping_stores_branches(ptr %p, i1 %i) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
|
|
; CHECK-LABEL: define void @partially_overlapping_stores_branches(
|
|
; CHECK-SAME: ptr captures(none) initializes((4, 8)) [[P:%.*]], i1 [[I:%.*]]) #[[ATTR1]] {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[A:%.*]] = load i32, ptr [[P]], align 4
|
|
; CHECK-NEXT: [[G:%.*]] = getelementptr i8, ptr [[P]], i64 4
|
|
; CHECK-NEXT: br i1 [[I]], label [[BB1:%.*]], label [[BB2:%.*]]
|
|
; CHECK: bb1:
|
|
; CHECK-NEXT: store i64 123, ptr [[G]], align 4
|
|
; CHECK-NEXT: br label [[END:%.*]]
|
|
; CHECK: bb2:
|
|
; CHECK-NEXT: store i64 321, ptr [[P]], align 4
|
|
; CHECK-NEXT: br label [[END]]
|
|
; CHECK: end:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
%a = load i32, ptr %p
|
|
%g = getelementptr i8, ptr %p, i64 4
|
|
br i1 %i, label %bb1, label %bb2
|
|
bb1:
|
|
store i64 123, ptr %g
|
|
br label %end
|
|
bb2:
|
|
store i64 321, ptr %p
|
|
br label %end
|
|
end:
|
|
ret void
|
|
}
|
|
|
|
define void @non_overlapping_stores_branches(ptr %p, i1 %i) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
|
|
; CHECK-LABEL: define void @non_overlapping_stores_branches(
|
|
; CHECK-SAME: ptr writeonly captures(none) [[P:%.*]], i1 [[I:%.*]]) #[[ATTR0]] {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[G:%.*]] = getelementptr i8, ptr [[P]], i64 8
|
|
; CHECK-NEXT: br i1 [[I]], label [[BB1:%.*]], label [[BB2:%.*]]
|
|
; CHECK: bb1:
|
|
; CHECK-NEXT: store i64 123, ptr [[G]], align 4
|
|
; CHECK-NEXT: br label [[END:%.*]]
|
|
; CHECK: bb2:
|
|
; CHECK-NEXT: store i64 321, ptr [[P]], align 4
|
|
; CHECK-NEXT: br label [[END]]
|
|
; CHECK: end:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
%g = getelementptr i8, ptr %p, i64 8
|
|
br i1 %i, label %bb1, label %bb2
|
|
bb1:
|
|
store i64 123, ptr %g
|
|
br label %end
|
|
bb2:
|
|
store i64 321, ptr %p
|
|
br label %end
|
|
end:
|
|
ret void
|
|
}
|
|
|
|
define void @dominating_store(ptr %p, i1 %i) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
|
|
; CHECK-LABEL: define void @dominating_store(
|
|
; CHECK-SAME: ptr writeonly captures(none) initializes((0, 8)) [[P:%.*]], i1 [[I:%.*]]) #[[ATTR0]] {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 [[I]], label [[BB1:%.*]], label [[BB2:%.*]]
|
|
; CHECK: bb1:
|
|
; CHECK-NEXT: br label [[END:%.*]]
|
|
; CHECK: bb2:
|
|
; CHECK-NEXT: br label [[END]]
|
|
; CHECK: end:
|
|
; CHECK-NEXT: store i64 321, ptr [[P]], align 4
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
br i1 %i, label %bb1, label %bb2
|
|
bb1:
|
|
br label %end
|
|
bb2:
|
|
br label %end
|
|
end:
|
|
store i64 321, ptr %p
|
|
ret void
|
|
}
|
|
|
|
define void @call_clobber_on_one_branch(ptr %p, i1 %i) {
|
|
; CHECK-LABEL: define void @call_clobber_on_one_branch(
|
|
; CHECK-SAME: ptr [[P:%.*]], i1 [[I:%.*]]) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 [[I]], label [[BB1:%.*]], label [[BB2:%.*]]
|
|
; CHECK: bb1:
|
|
; CHECK-NEXT: br label [[END:%.*]]
|
|
; CHECK: bb2:
|
|
; CHECK-NEXT: call void @use(ptr [[P]])
|
|
; CHECK-NEXT: br label [[END]]
|
|
; CHECK: end:
|
|
; CHECK-NEXT: store i64 321, ptr [[P]], align 4
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
br i1 %i, label %bb1, label %bb2
|
|
bb1:
|
|
br label %end
|
|
bb2:
|
|
call void @use(ptr %p)
|
|
br label %end
|
|
end:
|
|
store i64 321, ptr %p
|
|
ret void
|
|
}
|
|
|
|
define void @merge_existing_initializes(ptr initializes((33, 36)) %p) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
|
|
; CHECK-LABEL: define void @merge_existing_initializes(
|
|
; CHECK-SAME: ptr writeonly captures(none) initializes((0, 8), (33, 36)) [[P:%.*]]) #[[ATTR0]] {
|
|
; CHECK-NEXT: store i64 123, ptr [[P]], align 4
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
store i64 123, ptr %p
|
|
ret void
|
|
}
|
|
|
|
define void @negative_offset(ptr %p) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
|
|
; CHECK-LABEL: define void @negative_offset(
|
|
; CHECK-SAME: ptr writeonly captures(none) initializes((-5, 3)) [[P:%.*]]) #[[ATTR0]] {
|
|
; CHECK-NEXT: [[G:%.*]] = getelementptr i8, ptr [[P]], i64 -5
|
|
; CHECK-NEXT: store i64 123, ptr [[G]], align 4
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%g = getelementptr i8, ptr %p, i64 -5
|
|
store i64 123, ptr %g
|
|
ret void
|
|
}
|
|
|
|
define void @non_const_gep(ptr %p, i64 %i) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
|
|
; CHECK-LABEL: define void @non_const_gep(
|
|
; CHECK-SAME: ptr writeonly captures(none) initializes((0, 8)) [[P:%.*]], i64 [[I:%.*]]) #[[ATTR0]] {
|
|
; CHECK-NEXT: [[G:%.*]] = getelementptr i8, ptr [[P]], i64 [[I]]
|
|
; CHECK-NEXT: store i64 123, ptr [[G]], align 4
|
|
; CHECK-NEXT: store i64 123, ptr [[P]], align 4
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%g = getelementptr i8, ptr %p, i64 %i
|
|
store i64 123, ptr %g
|
|
store i64 123, ptr %p
|
|
ret void
|
|
}
|
|
|
|
define void @call_clobber_in_entry_block(ptr %p, i1 %i) {
|
|
; CHECK-LABEL: define void @call_clobber_in_entry_block(
|
|
; CHECK-SAME: ptr [[P:%.*]], i1 [[I:%.*]]) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: call void @use(ptr [[P]])
|
|
; CHECK-NEXT: br i1 [[I]], label [[BB1:%.*]], label [[BB2:%.*]]
|
|
; CHECK: bb1:
|
|
; CHECK-NEXT: store i64 123, ptr [[P]], align 4
|
|
; CHECK-NEXT: br label [[END:%.*]]
|
|
; CHECK: bb2:
|
|
; CHECK-NEXT: store i64 321, ptr [[P]], align 4
|
|
; CHECK-NEXT: br label [[END]]
|
|
; CHECK: end:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
call void @use(ptr %p)
|
|
br i1 %i, label %bb1, label %bb2
|
|
bb1:
|
|
store i64 123, ptr %p
|
|
br label %end
|
|
bb2:
|
|
store i64 321, ptr %p
|
|
br label %end
|
|
end:
|
|
ret void
|
|
}
|
|
|
|
declare void @g1(ptr initializes((0, 4)) %p)
|
|
declare void @g2(ptr initializes((8, 12)) %p)
|
|
declare void @g3(ptr initializes((0, 4)) writeonly nocapture %p)
|
|
|
|
define void @call_initializes(ptr %p) {
|
|
; CHECK-LABEL: define void @call_initializes(
|
|
; CHECK-SAME: ptr initializes((0, 4)) [[P:%.*]]) {
|
|
; CHECK-NEXT: call void @g1(ptr [[P]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
call void @g1(ptr %p)
|
|
ret void
|
|
}
|
|
|
|
define void @call_initializes_clobber(ptr %p) {
|
|
; CHECK-LABEL: define void @call_initializes_clobber(
|
|
; CHECK-SAME: ptr initializes((0, 4)) [[P:%.*]]) {
|
|
; CHECK-NEXT: call void @g1(ptr [[P]])
|
|
; CHECK-NEXT: call void @g2(ptr [[P]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
call void @g1(ptr %p)
|
|
call void @g2(ptr %p)
|
|
ret void
|
|
}
|
|
|
|
define void @call_initializes_no_clobber_writeonly_nocapture(ptr %p) {
|
|
; CHECK-LABEL: define void @call_initializes_no_clobber_writeonly_nocapture(
|
|
; CHECK-SAME: ptr initializes((0, 4), (8, 12)) [[P:%.*]]) {
|
|
; CHECK-NEXT: call void @g3(ptr [[P]])
|
|
; CHECK-NEXT: call void @g2(ptr [[P]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
call void @g3(ptr %p)
|
|
call void @g2(ptr %p)
|
|
ret void
|
|
}
|
|
|
|
define void @call_initializes_escape_bundle(ptr %p) {
|
|
; CHECK-LABEL: define void @call_initializes_escape_bundle(
|
|
; CHECK-SAME: ptr [[P:%.*]]) {
|
|
; CHECK-NEXT: call void @g1(ptr [[P]]) [ "unknown"(ptr [[P]]) ]
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
call void @g1(ptr %p) ["unknown"(ptr %p)]
|
|
ret void
|
|
}
|
|
|
|
define void @access_bundle() {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
|
|
; CHECK-LABEL: define void @access_bundle(
|
|
; CHECK-SAME: ) #[[ATTR3:[0-9]+]] {
|
|
; CHECK-NEXT: [[SINK:%.*]] = alloca i64, align 8
|
|
; CHECK-NEXT: store i64 123, ptr [[SINK]], align 4
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%sink = alloca i64, align 8
|
|
store i64 123, ptr %sink
|
|
ret void
|
|
}
|
|
|
|
define void @call_operand_bundle(ptr %p) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn
|
|
; CHECK-LABEL: define void @call_operand_bundle(
|
|
; CHECK-SAME: ptr [[P:%.*]]) #[[ATTR4:[0-9]+]] {
|
|
; CHECK-NEXT: call void @access_bundle() [ "unknown"(ptr [[P]]) ]
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
call void @access_bundle() ["unknown"(ptr %p)]
|
|
ret void
|
|
}
|
|
|
|
declare void @llvm.memset(ptr, i8, i64 ,i1)
|
|
|
|
define void @memset(ptr %p) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
|
|
; CHECK-LABEL: define void @memset(
|
|
; CHECK-SAME: ptr writeonly captures(none) initializes((0, 9)) [[P:%.*]]) #[[ATTR0]] {
|
|
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr [[P]], i8 2, i64 9, i1 false)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
call void @llvm.memset(ptr %p, i8 2, i64 9, i1 false)
|
|
ret void
|
|
}
|
|
|
|
define void @memset_offset(ptr %p) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
|
|
; CHECK-LABEL: define void @memset_offset(
|
|
; CHECK-SAME: ptr writeonly captures(none) initializes((3, 12)) [[P:%.*]]) #[[ATTR0]] {
|
|
; CHECK-NEXT: [[G:%.*]] = getelementptr i8, ptr [[P]], i64 3
|
|
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr [[G]], i8 2, i64 9, i1 false)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%g = getelementptr i8, ptr %p, i64 3
|
|
call void @llvm.memset(ptr %g, i8 2, i64 9, i1 false)
|
|
ret void
|
|
}
|
|
|
|
define void @memset_neg(ptr %p) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
|
|
; CHECK-LABEL: define void @memset_neg(
|
|
; CHECK-SAME: ptr writeonly captures(none) [[P:%.*]]) #[[ATTR0]] {
|
|
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr [[P]], i8 2, i64 -1, i1 false)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
call void @llvm.memset(ptr %p, i8 2, i64 -1, i1 false)
|
|
ret void
|
|
}
|
|
|
|
define void @memset_volatile(ptr %p) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: write, inaccessiblemem: readwrite)
|
|
; CHECK-LABEL: define void @memset_volatile(
|
|
; CHECK-SAME: ptr writeonly [[P:%.*]]) #[[ATTR5:[0-9]+]] {
|
|
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr [[P]], i8 2, i64 9, i1 true)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
call void @llvm.memset(ptr %p, i8 2, i64 9, i1 true)
|
|
ret void
|
|
}
|
|
|
|
define void @memset_non_constant(ptr %p, i64 %i) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
|
|
; CHECK-LABEL: define void @memset_non_constant(
|
|
; CHECK-SAME: ptr writeonly captures(none) [[P:%.*]], i64 [[I:%.*]]) #[[ATTR0]] {
|
|
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr [[P]], i8 2, i64 [[I]], i1 false)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
call void @llvm.memset(ptr %p, i8 2, i64 %i, i1 false)
|
|
ret void
|
|
}
|
|
|
|
declare void @llvm.memcpy(ptr, ptr, i64 ,i1)
|
|
|
|
define void @memcpy(ptr %p, ptr %p2) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
|
|
; CHECK-LABEL: define void @memcpy(
|
|
; CHECK-SAME: ptr writeonly captures(none) initializes((0, 9)) [[P:%.*]], ptr readonly captures(none) [[P2:%.*]]) #[[ATTR1]] {
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[P]], ptr [[P2]], i64 9, i1 false)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
call void @llvm.memcpy(ptr %p, ptr %p2, i64 9, i1 false)
|
|
ret void
|
|
}
|
|
|
|
define void @memcpy_volatile(ptr %p, ptr %p2) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite, inaccessiblemem: readwrite)
|
|
; CHECK-LABEL: define void @memcpy_volatile(
|
|
; CHECK-SAME: ptr writeonly [[P:%.*]], ptr readonly [[P2:%.*]]) #[[ATTR6:[0-9]+]] {
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[P]], ptr [[P2]], i64 9, i1 true)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
call void @llvm.memcpy(ptr %p, ptr %p2, i64 9, i1 true)
|
|
ret void
|
|
}
|
|
|
|
define void @memcpy_offset(ptr %p, ptr %p2) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
|
|
; CHECK-LABEL: define void @memcpy_offset(
|
|
; CHECK-SAME: ptr writeonly captures(none) initializes((3, 12)) [[P:%.*]], ptr readonly captures(none) [[P2:%.*]]) #[[ATTR1]] {
|
|
; CHECK-NEXT: [[G:%.*]] = getelementptr i8, ptr [[P]], i64 3
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[G]], ptr [[P2]], i64 9, i1 false)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%g = getelementptr i8, ptr %p, i64 3
|
|
call void @llvm.memcpy(ptr %g, ptr %p2, i64 9, i1 false)
|
|
ret void
|
|
}
|
|
|
|
define void @memcpy_src(ptr %p, ptr %p2) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
|
|
; CHECK-LABEL: define void @memcpy_src(
|
|
; CHECK-SAME: ptr captures(none) initializes((96, 128)) [[P:%.*]], ptr captures(none) initializes((0, 96)) [[P2:%.*]]) #[[ATTR1]] {
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[P2]], ptr [[P]], i64 96, i1 false)
|
|
; CHECK-NEXT: [[G:%.*]] = getelementptr i8, ptr [[P]], i64 64
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[G]], ptr [[P2]], i64 64, i1 false)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
call void @llvm.memcpy(ptr %p2, ptr %p, i64 96, i1 false)
|
|
%g = getelementptr i8, ptr %p, i64 64
|
|
call void @llvm.memcpy(ptr %g, ptr %p2, i64 64, i1 false)
|
|
ret void
|
|
}
|
|
|
|
define void @memcpy_non_constant(ptr %p, ptr %p2, i64 %i) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
|
|
; CHECK-LABEL: define void @memcpy_non_constant(
|
|
; CHECK-SAME: ptr writeonly captures(none) [[P:%.*]], ptr readonly captures(none) [[P2:%.*]], i64 [[I:%.*]]) #[[ATTR1]] {
|
|
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[P]], ptr [[P2]], i64 [[I]], i1 false)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
call void @llvm.memcpy(ptr %p, ptr %p2, i64 %i, i1 false)
|
|
ret void
|
|
}
|
|
|
|
declare void @llvm.memmove(ptr, ptr, i64 ,i1)
|
|
|
|
define void @memmove(ptr %p, ptr %p2) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
|
|
; CHECK-LABEL: define void @memmove(
|
|
; CHECK-SAME: ptr writeonly captures(none) initializes((0, 9)) [[P:%.*]], ptr readonly captures(none) [[P2:%.*]]) #[[ATTR1]] {
|
|
; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr [[P]], ptr [[P2]], i64 9, i1 false)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
call void @llvm.memmove(ptr %p, ptr %p2, i64 9, i1 false)
|
|
ret void
|
|
}
|
|
|
|
define void @memmove_volatile(ptr %p, ptr %p2) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite, inaccessiblemem: readwrite)
|
|
; CHECK-LABEL: define void @memmove_volatile(
|
|
; CHECK-SAME: ptr writeonly [[P:%.*]], ptr readonly [[P2:%.*]]) #[[ATTR6]] {
|
|
; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr [[P]], ptr [[P2]], i64 9, i1 true)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
call void @llvm.memmove(ptr %p, ptr %p2, i64 9, i1 true)
|
|
ret void
|
|
}
|
|
|
|
define void @memmove_offset(ptr %p, ptr %p2) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
|
|
; CHECK-LABEL: define void @memmove_offset(
|
|
; CHECK-SAME: ptr writeonly captures(none) initializes((3, 12)) [[P:%.*]], ptr readonly captures(none) [[P2:%.*]]) #[[ATTR1]] {
|
|
; CHECK-NEXT: [[G:%.*]] = getelementptr i8, ptr [[P]], i64 3
|
|
; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr [[G]], ptr [[P2]], i64 9, i1 false)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%g = getelementptr i8, ptr %p, i64 3
|
|
call void @llvm.memmove(ptr %g, ptr %p2, i64 9, i1 false)
|
|
ret void
|
|
}
|
|
|
|
define void @memmove_src(ptr %p, ptr %p2) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
|
|
; CHECK-LABEL: define void @memmove_src(
|
|
; CHECK-SAME: ptr captures(none) initializes((96, 128)) [[P:%.*]], ptr captures(none) initializes((0, 96)) [[P2:%.*]]) #[[ATTR1]] {
|
|
; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr [[P2]], ptr [[P]], i64 96, i1 false)
|
|
; CHECK-NEXT: [[G:%.*]] = getelementptr i8, ptr [[P]], i64 64
|
|
; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr [[G]], ptr [[P2]], i64 64, i1 false)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
call void @llvm.memmove(ptr %p2, ptr %p, i64 96, i1 false)
|
|
%g = getelementptr i8, ptr %p, i64 64
|
|
call void @llvm.memmove(ptr %g, ptr %p2, i64 64, i1 false)
|
|
ret void
|
|
}
|
|
|
|
define void @memmove_non_constant(ptr %p, ptr %p2, i64 %i) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
|
|
; CHECK-LABEL: define void @memmove_non_constant(
|
|
; CHECK-SAME: ptr writeonly captures(none) [[P:%.*]], ptr readonly captures(none) [[P2:%.*]], i64 [[I:%.*]]) #[[ATTR1]] {
|
|
; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr [[P]], ptr [[P2]], i64 [[I]], i1 false)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
call void @llvm.memmove(ptr %p, ptr %p2, i64 %i, i1 false)
|
|
ret void
|
|
}
|
|
|
|
define void @callee_byval(ptr byval(i32) %p) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
|
|
; CHECK-LABEL: define void @callee_byval(
|
|
; CHECK-SAME: ptr writeonly byval(i32) captures(none) initializes((0, 4)) [[P:%.*]]) #[[ATTR0]] {
|
|
; CHECK-NEXT: store i32 0, ptr [[P]], align 4
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
store i32 0, ptr %p
|
|
ret void
|
|
}
|
|
|
|
define void @caller_byval(ptr %p) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
|
|
; CHECK-LABEL: define void @caller_byval(
|
|
; CHECK-SAME: ptr readonly captures(none) [[P:%.*]]) #[[ATTR0]] {
|
|
; CHECK-NEXT: call void @callee_byval(ptr byval(i32) [[P]])
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
call void @callee_byval(ptr byval(i32) %p)
|
|
ret void
|
|
}
|
|
|
|
define void @memset_offset_0_size_0(ptr %dst, ptr %src) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
|
|
; CHECK-LABEL: define void @memset_offset_0_size_0(
|
|
; CHECK-SAME: ptr writeonly captures(none) [[DST:%.*]], ptr readonly captures(none) [[SRC:%.*]]) #[[ATTR1]] {
|
|
; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr [[DST]], ptr [[SRC]], i64 0, i1 false)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
call void @llvm.memmove.p0.p0.i64(ptr %dst, ptr %src, i64 0, i1 false)
|
|
ret void
|
|
}
|
|
|
|
define void @memset_offset_1_size_0(ptr %dst, ptr %src) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
|
|
; CHECK-LABEL: define void @memset_offset_1_size_0(
|
|
; CHECK-SAME: ptr writeonly captures(none) [[DST:%.*]], ptr readonly captures(none) [[SRC:%.*]]) #[[ATTR1]] {
|
|
; CHECK-NEXT: [[DST_1:%.*]] = getelementptr inbounds i8, ptr [[DST]], i64 1
|
|
; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr [[DST_1]], ptr [[SRC]], i64 0, i1 false)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%dst.1 = getelementptr inbounds i8, ptr %dst, i64 1
|
|
call void @llvm.memmove.p0.p0.i64(ptr %dst.1, ptr %src, i64 0, i1 false)
|
|
ret void
|
|
}
|
|
|
|
; We should bail if the range overflows a singed 64-bit int.
|
|
define void @range_overflows_signed_64_bit_int(ptr %arg) {
|
|
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
|
|
; CHECK-LABEL: define void @range_overflows_signed_64_bit_int(
|
|
; CHECK-SAME: ptr writeonly captures(none) [[ARG:%.*]]) #[[ATTR0]] {
|
|
; CHECK-NEXT: [[GETELEMENTPTR:%.*]] = getelementptr i8, ptr [[ARG]], i64 9223372036854775804
|
|
; CHECK-NEXT: store i32 0, ptr [[GETELEMENTPTR]], align 4
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%getelementptr = getelementptr i8, ptr %arg, i64 9223372036854775804
|
|
store i32 0, ptr %getelementptr
|
|
ret void
|
|
}
|