diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp index e24f27b020ab..a216711f8f0c 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -6768,7 +6768,7 @@ static bool isMemSrcFromConstant(SDValue Src, ConstantDataArraySlice &Slice) { GlobalAddressSDNode *G = nullptr; if (Src.getOpcode() == ISD::GlobalAddress) G = cast(Src); - else if (Src.getOpcode() == ISD::ADD && + else if ((Src.getOpcode() == ISD::ADD || Src.getOpcode() == ISD::PTRADD) && Src.getOperand(0).getOpcode() == ISD::GlobalAddress && Src.getOperand(1).getOpcode() == ISD::Constant) { G = cast(Src.getOperand(0)); @@ -6887,7 +6887,7 @@ static SDValue getMemcpyLoadsAndStores( // TODO: the frontend/optimization passes probably shouldn't emit // must-preserve-tags for such small memcpys auto CapTy = TLI.cheriCapabilityType(); - if (CapTy.isValid()) { + if (CapTy.isValid() && !Op.isMemset()) { const uint64_t CapSize = CapTy.getStoreSize(); if (PreserveTags == PreserveCheriTags::Required && !ReachedLimit && Size >= CapSize && (!FoundLowering || !MemOps[0].isFatPointer())) { diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp index e376a0f2a67d..037a99cf8031 100644 --- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp @@ -208,6 +208,7 @@ bool TargetLowering::findOptimalMemOpLowering( // XXXAR: (ab)use MVT::isVoid to indicate that a memcpy call must be made if (VT == MVT::isVoid) { + assert(!Op.isMemset() && "MVT::isVoid should only be used for copies"); return false; // cannot lower as memops } // If the type is a fat pointer, then forcibly disable overlap. diff --git a/llvm/test/CodeGen/CHERI-Generic/Inputs/memcpy-from-constant.ll b/llvm/test/CodeGen/CHERI-Generic/Inputs/memcpy-from-constant.ll new file mode 100644 index 000000000000..d5df3382ad8b --- /dev/null +++ b/llvm/test/CodeGen/CHERI-Generic/Inputs/memcpy-from-constant.ll @@ -0,0 +1,93 @@ +;; Copying from a zero constant can be converted to a memset (even with the tag preservation flags) +; RUN: llc @PURECAP_HARDFLOAT_ARGS@ < %s -o - | FileCheck %s + +@a = internal addrspace(200) constant ptr addrspace(200) null +@b = internal addrspace(200) constant ptr addrspace(200) null +@zero_constant = internal addrspace(200) constant [5 x ptr addrspace(200)] zeroinitializer +@constant_ptrs = internal addrspace(200) constant [2 x ptr addrspace(200)] [ptr addrspace(200) @a, ptr addrspace(200) @b] + +declare void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) noalias nocapture writeonly, ptr addrspace(200) noalias nocapture readonly, i64, i1 immarg) addrspace(200) #0 + +define linkonce_odr void @copy_from_zero_constant(ptr addrspace(200) %dst) addrspace(200) { +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align @CAP_BYTES@ %dst, ptr addrspace(200) align @CAP_BYTES@ @zero_constant, i64 @CAP_BYTES@, i1 false) + ret void +} + +define linkonce_odr void @copy_from_zero_constant_with_offset(ptr addrspace(200) %dst) addrspace(200) { +do.body: + %src = getelementptr inbounds i8, ptr addrspace(200) @zero_constant, i64 @CAP_BYTES@ + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align @CAP_BYTES@ %dst, ptr addrspace(200) align @CAP_BYTES@ %src, i64 @CAP_BYTES@, i1 false) + ret void +} + +define linkonce_odr void @copy_from_large_zero_constant(ptr addrspace(200) %dst) addrspace(200) { +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align @CAP_BYTES@ %dst, ptr addrspace(200) align @CAP_BYTES@ @zero_constant, i64 @CAP_RANGE_BYTES@, i1 false) + ret void +} + +define linkonce_odr void @copy_from_ptr_constant(ptr addrspace(200) %dst) addrspace(200) { +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align @CAP_BYTES@ %dst, ptr addrspace(200) align @CAP_BYTES@ @constant_ptrs, i64 @CAP_BYTES@, i1 false) + ret void +} + +define linkonce_odr void @copy_from_ptr_constant_with_offset(ptr addrspace(200) %dst) addrspace(200) { +do.body: + %src = getelementptr inbounds i8, ptr addrspace(200) @constant_ptrs, i64 @CAP_BYTES@ + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align @CAP_BYTES@ %dst, ptr addrspace(200) align @CAP_BYTES@ %src, i64 @CAP_BYTES@, i1 false) + ret void +} + +;; Run the same tests again this time with must_preserve_tags to check that we don't call memcpy(). + +define linkonce_odr void @copy_from_zero_constant_preserve(ptr addrspace(200) %dst) addrspace(200) { +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align @CAP_BYTES@ %dst, ptr addrspace(200) align @CAP_BYTES@ @zero_constant, i64 @CAP_BYTES@, i1 false) #1 + ret void +} + +define linkonce_odr void @copy_from_zero_constant_with_offset_preserve(ptr addrspace(200) %dst) addrspace(200) { +do.body: + %src = getelementptr inbounds i8, ptr addrspace(200) @zero_constant, i64 @CAP_BYTES@ + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align @CAP_BYTES@ %dst, ptr addrspace(200) align @CAP_BYTES@ %src, i64 @CAP_BYTES@, i1 false) #1 + ret void +} + +define linkonce_odr void @copy_from_large_zero_constant_preserve(ptr addrspace(200) %dst) addrspace(200) { +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align @CAP_BYTES@ %dst, ptr addrspace(200) align @CAP_BYTES@ @zero_constant, i64 @CAP_RANGE_BYTES@, i1 false) #1 + ret void +} + +define linkonce_odr void @copy_from_ptr_constant_preserve(ptr addrspace(200) %dst) addrspace(200) { +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align @CAP_BYTES@ %dst, ptr addrspace(200) align @CAP_BYTES@ @constant_ptrs, i64 @CAP_BYTES@, i1 false) #1 + ret void +} + +define linkonce_odr void @copy_from_ptr_constant_with_offset_preserve(ptr addrspace(200) %dst) addrspace(200) { +do.body: + %src = getelementptr inbounds i8, ptr addrspace(200) @constant_ptrs, i64 @CAP_BYTES@ + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align @CAP_BYTES@ %dst, ptr addrspace(200) align @CAP_BYTES@ %src, i64 @CAP_BYTES@, i1 false) #1 + ret void +} + +;; Finally, check copying from a zero constant with insufficient known alignment. +;; We should be able to emit this inline since a zero constant source never has tags. + +define linkonce_odr void @copy_from_underaligned_zero_constant(ptr addrspace(200) %dst) addrspace(200) { +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align @CAP_RANGE_BYTES@ %dst, ptr addrspace(200) align @CAP_RANGE_BYTES@ @zero_constant, i64 @CAP_BYTES@, i1 false) #1 + ret void +} + +define linkonce_odr void @copy_from_underaligned_zero_constant_preserve(ptr addrspace(200) %dst) addrspace(200) { +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align @CAP_RANGE_BYTES@ %dst, ptr addrspace(200) align @CAP_RANGE_BYTES@ @zero_constant, i64 @CAP_BYTES@, i1 false) #1 + ret void +} + +attributes #0 = { argmemonly nocallback nofree nounwind willreturn } +attributes #1 = { must_preserve_cheri_tags "frontend-memtransfer-type"="'const UChar * __capability' (aka 'const char16_t * __capability')" } diff --git a/llvm/test/CodeGen/CHERI-Generic/MIPS/memcpy-from-constant.ll b/llvm/test/CodeGen/CHERI-Generic/MIPS/memcpy-from-constant.ll new file mode 100644 index 000000000000..fd4dd1a78385 --- /dev/null +++ b/llvm/test/CodeGen/CHERI-Generic/MIPS/memcpy-from-constant.ll @@ -0,0 +1,165 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes --force-update +; DO NOT EDIT -- This file was generated from test/CodeGen/CHERI-Generic/Inputs/memcpy-from-constant.ll +;; Copying from a zero constant can be converted to a memset (even with the tag preservation flags) +; RUN: llc -mtriple=mips64 -mcpu=cheri128 -mattr=+cheri128 --relocation-model=pic -target-abi purecap < %s -o - | FileCheck %s + +@a = internal addrspace(200) constant ptr addrspace(200) null +@b = internal addrspace(200) constant ptr addrspace(200) null +@zero_constant = internal addrspace(200) constant [5 x ptr addrspace(200)] zeroinitializer +@constant_ptrs = internal addrspace(200) constant [2 x ptr addrspace(200)] [ptr addrspace(200) @a, ptr addrspace(200) @b] + +declare void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) noalias nocapture writeonly, ptr addrspace(200) noalias nocapture readonly, i64, i1 immarg) addrspace(200) #0 + +define linkonce_odr void @copy_from_zero_constant(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_zero_constant: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: cjr $c17 +; CHECK-NEXT: csc $cnull, $zero, 0($c3) +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 16 %dst, ptr addrspace(200) align 16 @zero_constant, i64 16, i1 false) + ret void +} + +define linkonce_odr void @copy_from_zero_constant_with_offset(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_zero_constant_with_offset: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: cjr $c17 +; CHECK-NEXT: csc $cnull, $zero, 0($c3) +do.body: + %src = getelementptr inbounds i8, ptr addrspace(200) @zero_constant, i64 16 + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 16 %dst, ptr addrspace(200) align 16 %src, i64 16, i1 false) + ret void +} + +define linkonce_odr void @copy_from_large_zero_constant(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_large_zero_constant: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: cjr $c17 +; CHECK-NEXT: csd $zero, $zero, 0($c3) +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 16 %dst, ptr addrspace(200) align 16 @zero_constant, i64 8, i1 false) + ret void +} + +define linkonce_odr void @copy_from_ptr_constant(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_ptr_constant: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: lui $1, %pcrel_hi(_CHERI_CAPABILITY_TABLE_-8) +; CHECK-NEXT: daddiu $1, $1, %pcrel_lo(_CHERI_CAPABILITY_TABLE_-4) +; CHECK-NEXT: cgetpccincoffset $c1, $1 +; CHECK-NEXT: clcbi $c1, %captab20(constant_ptrs)($c1) +; CHECK-NEXT: clc $c1, $zero, 0($c1) +; CHECK-NEXT: cjr $c17 +; CHECK-NEXT: csc $c1, $zero, 0($c3) +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 16 %dst, ptr addrspace(200) align 16 @constant_ptrs, i64 16, i1 false) + ret void +} + +define linkonce_odr void @copy_from_ptr_constant_with_offset(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_ptr_constant_with_offset: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: lui $1, %pcrel_hi(_CHERI_CAPABILITY_TABLE_-8) +; CHECK-NEXT: daddiu $1, $1, %pcrel_lo(_CHERI_CAPABILITY_TABLE_-4) +; CHECK-NEXT: cgetpccincoffset $c1, $1 +; CHECK-NEXT: clcbi $c1, %captab20(constant_ptrs)($c1) +; CHECK-NEXT: clc $c1, $zero, 16($c1) +; CHECK-NEXT: cjr $c17 +; CHECK-NEXT: csc $c1, $zero, 0($c3) +do.body: + %src = getelementptr inbounds i8, ptr addrspace(200) @constant_ptrs, i64 16 + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 16 %dst, ptr addrspace(200) align 16 %src, i64 16, i1 false) + ret void +} + +;; Run the same tests again this time with must_preserve_tags to check that we don't call memcpy(). + +define linkonce_odr void @copy_from_zero_constant_preserve(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_zero_constant_preserve: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: cjr $c17 +; CHECK-NEXT: csc $cnull, $zero, 0($c3) +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 16 %dst, ptr addrspace(200) align 16 @zero_constant, i64 16, i1 false) #1 + ret void +} + +define linkonce_odr void @copy_from_zero_constant_with_offset_preserve(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_zero_constant_with_offset_preserve: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: cjr $c17 +; CHECK-NEXT: csc $cnull, $zero, 0($c3) +do.body: + %src = getelementptr inbounds i8, ptr addrspace(200) @zero_constant, i64 16 + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 16 %dst, ptr addrspace(200) align 16 %src, i64 16, i1 false) #1 + ret void +} + +define linkonce_odr void @copy_from_large_zero_constant_preserve(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_large_zero_constant_preserve: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: cjr $c17 +; CHECK-NEXT: csd $zero, $zero, 0($c3) +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 16 %dst, ptr addrspace(200) align 16 @zero_constant, i64 8, i1 false) #1 + ret void +} + +define linkonce_odr void @copy_from_ptr_constant_preserve(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_ptr_constant_preserve: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: lui $1, %pcrel_hi(_CHERI_CAPABILITY_TABLE_-8) +; CHECK-NEXT: daddiu $1, $1, %pcrel_lo(_CHERI_CAPABILITY_TABLE_-4) +; CHECK-NEXT: cgetpccincoffset $c1, $1 +; CHECK-NEXT: clcbi $c1, %captab20(constant_ptrs)($c1) +; CHECK-NEXT: clc $c1, $zero, 0($c1) +; CHECK-NEXT: cjr $c17 +; CHECK-NEXT: csc $c1, $zero, 0($c3) +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 16 %dst, ptr addrspace(200) align 16 @constant_ptrs, i64 16, i1 false) #1 + ret void +} + +define linkonce_odr void @copy_from_ptr_constant_with_offset_preserve(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_ptr_constant_with_offset_preserve: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: lui $1, %pcrel_hi(_CHERI_CAPABILITY_TABLE_-8) +; CHECK-NEXT: daddiu $1, $1, %pcrel_lo(_CHERI_CAPABILITY_TABLE_-4) +; CHECK-NEXT: cgetpccincoffset $c1, $1 +; CHECK-NEXT: clcbi $c1, %captab20(constant_ptrs)($c1) +; CHECK-NEXT: clc $c1, $zero, 16($c1) +; CHECK-NEXT: cjr $c17 +; CHECK-NEXT: csc $c1, $zero, 0($c3) +do.body: + %src = getelementptr inbounds i8, ptr addrspace(200) @constant_ptrs, i64 16 + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 16 %dst, ptr addrspace(200) align 16 %src, i64 16, i1 false) #1 + ret void +} + +;; Finally, check copying from a zero constant with insufficient known alignment. +;; We should be able to emit this inline since a zero constant source never has tags. + +define linkonce_odr void @copy_from_underaligned_zero_constant(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_underaligned_zero_constant: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: csd $zero, $zero, 0($c3) +; CHECK-NEXT: cjr $c17 +; CHECK-NEXT: csd $zero, $zero, 8($c3) +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 8 %dst, ptr addrspace(200) align 8 @zero_constant, i64 16, i1 false) #1 + ret void +} + +define linkonce_odr void @copy_from_underaligned_zero_constant_preserve(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_underaligned_zero_constant_preserve: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: csd $zero, $zero, 0($c3) +; CHECK-NEXT: cjr $c17 +; CHECK-NEXT: csd $zero, $zero, 8($c3) +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 8 %dst, ptr addrspace(200) align 8 @zero_constant, i64 16, i1 false) #1 + ret void +} + +attributes #0 = { argmemonly nocallback nofree nounwind willreturn } +attributes #1 = { must_preserve_cheri_tags "frontend-memtransfer-type"="'const UChar * __capability' (aka 'const char16_t * __capability')" } diff --git a/llvm/test/CodeGen/CHERI-Generic/RISCV32/memcpy-from-constant.ll b/llvm/test/CodeGen/CHERI-Generic/RISCV32/memcpy-from-constant.ll new file mode 100644 index 000000000000..323d1e2fd94d --- /dev/null +++ b/llvm/test/CodeGen/CHERI-Generic/RISCV32/memcpy-from-constant.ll @@ -0,0 +1,165 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes --force-update +; DO NOT EDIT -- This file was generated from test/CodeGen/CHERI-Generic/Inputs/memcpy-from-constant.ll +;; Copying from a zero constant can be converted to a memset (even with the tag preservation flags) +; RUN: llc -mtriple=riscv32 --relocation-model=pic -target-abi il32pc64f -mattr=+xcheri,+cap-mode,+f < %s -o - | FileCheck %s + +@a = internal addrspace(200) constant ptr addrspace(200) null +@b = internal addrspace(200) constant ptr addrspace(200) null +@zero_constant = internal addrspace(200) constant [5 x ptr addrspace(200)] zeroinitializer +@constant_ptrs = internal addrspace(200) constant [2 x ptr addrspace(200)] [ptr addrspace(200) @a, ptr addrspace(200) @b] + +declare void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) noalias nocapture writeonly, ptr addrspace(200) noalias nocapture readonly, i64, i1 immarg) addrspace(200) #0 + +define linkonce_odr void @copy_from_zero_constant(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_zero_constant: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: csc cnull, 0(ca0) +; CHECK-NEXT: cret +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 8 %dst, ptr addrspace(200) align 8 @zero_constant, i64 8, i1 false) + ret void +} + +define linkonce_odr void @copy_from_zero_constant_with_offset(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_zero_constant_with_offset: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: csc cnull, 0(ca0) +; CHECK-NEXT: cret +do.body: + %src = getelementptr inbounds i8, ptr addrspace(200) @zero_constant, i64 8 + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 8 %dst, ptr addrspace(200) align 8 %src, i64 8, i1 false) + ret void +} + +define linkonce_odr void @copy_from_large_zero_constant(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_large_zero_constant: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: csw zero, 0(ca0) +; CHECK-NEXT: cret +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 8 %dst, ptr addrspace(200) align 8 @zero_constant, i64 4, i1 false) + ret void +} + +define linkonce_odr void @copy_from_ptr_constant(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_ptr_constant: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: .LBB3_1: # %do.body +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: auipcc ca1, %captab_pcrel_hi(constant_ptrs) +; CHECK-NEXT: clc ca1, %pcrel_lo(.LBB3_1)(ca1) +; CHECK-NEXT: clc ca1, 0(ca1) +; CHECK-NEXT: csc ca1, 0(ca0) +; CHECK-NEXT: cret +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 8 %dst, ptr addrspace(200) align 8 @constant_ptrs, i64 8, i1 false) + ret void +} + +define linkonce_odr void @copy_from_ptr_constant_with_offset(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_ptr_constant_with_offset: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: .LBB4_1: # %do.body +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: auipcc ca1, %captab_pcrel_hi(constant_ptrs) +; CHECK-NEXT: clc ca1, %pcrel_lo(.LBB4_1)(ca1) +; CHECK-NEXT: clc ca1, 8(ca1) +; CHECK-NEXT: csc ca1, 0(ca0) +; CHECK-NEXT: cret +do.body: + %src = getelementptr inbounds i8, ptr addrspace(200) @constant_ptrs, i64 8 + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 8 %dst, ptr addrspace(200) align 8 %src, i64 8, i1 false) + ret void +} + +;; Run the same tests again this time with must_preserve_tags to check that we don't call memcpy(). + +define linkonce_odr void @copy_from_zero_constant_preserve(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_zero_constant_preserve: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: csc cnull, 0(ca0) +; CHECK-NEXT: cret +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 8 %dst, ptr addrspace(200) align 8 @zero_constant, i64 8, i1 false) #1 + ret void +} + +define linkonce_odr void @copy_from_zero_constant_with_offset_preserve(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_zero_constant_with_offset_preserve: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: csc cnull, 0(ca0) +; CHECK-NEXT: cret +do.body: + %src = getelementptr inbounds i8, ptr addrspace(200) @zero_constant, i64 8 + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 8 %dst, ptr addrspace(200) align 8 %src, i64 8, i1 false) #1 + ret void +} + +define linkonce_odr void @copy_from_large_zero_constant_preserve(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_large_zero_constant_preserve: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: csw zero, 0(ca0) +; CHECK-NEXT: cret +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 8 %dst, ptr addrspace(200) align 8 @zero_constant, i64 4, i1 false) #1 + ret void +} + +define linkonce_odr void @copy_from_ptr_constant_preserve(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_ptr_constant_preserve: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: .LBB8_1: # %do.body +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: auipcc ca1, %captab_pcrel_hi(constant_ptrs) +; CHECK-NEXT: clc ca1, %pcrel_lo(.LBB8_1)(ca1) +; CHECK-NEXT: clc ca1, 0(ca1) +; CHECK-NEXT: csc ca1, 0(ca0) +; CHECK-NEXT: cret +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 8 %dst, ptr addrspace(200) align 8 @constant_ptrs, i64 8, i1 false) #1 + ret void +} + +define linkonce_odr void @copy_from_ptr_constant_with_offset_preserve(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_ptr_constant_with_offset_preserve: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: .LBB9_1: # %do.body +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: auipcc ca1, %captab_pcrel_hi(constant_ptrs) +; CHECK-NEXT: clc ca1, %pcrel_lo(.LBB9_1)(ca1) +; CHECK-NEXT: clc ca1, 8(ca1) +; CHECK-NEXT: csc ca1, 0(ca0) +; CHECK-NEXT: cret +do.body: + %src = getelementptr inbounds i8, ptr addrspace(200) @constant_ptrs, i64 8 + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 8 %dst, ptr addrspace(200) align 8 %src, i64 8, i1 false) #1 + ret void +} + +;; Finally, check copying from a zero constant with insufficient known alignment. +;; We should be able to emit this inline since a zero constant source never has tags. + +define linkonce_odr void @copy_from_underaligned_zero_constant(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_underaligned_zero_constant: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: csw zero, 4(ca0) +; CHECK-NEXT: csw zero, 0(ca0) +; CHECK-NEXT: cret +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 4 %dst, ptr addrspace(200) align 4 @zero_constant, i64 8, i1 false) #1 + ret void +} + +define linkonce_odr void @copy_from_underaligned_zero_constant_preserve(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_underaligned_zero_constant_preserve: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: csw zero, 4(ca0) +; CHECK-NEXT: csw zero, 0(ca0) +; CHECK-NEXT: cret +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 4 %dst, ptr addrspace(200) align 4 @zero_constant, i64 8, i1 false) #1 + ret void +} + +attributes #0 = { argmemonly nocallback nofree nounwind willreturn } +attributes #1 = { must_preserve_cheri_tags "frontend-memtransfer-type"="'const UChar * __capability' (aka 'const char16_t * __capability')" } diff --git a/llvm/test/CodeGen/CHERI-Generic/RISCV64/memcpy-from-constant.ll b/llvm/test/CodeGen/CHERI-Generic/RISCV64/memcpy-from-constant.ll new file mode 100644 index 000000000000..663a72de4dcc --- /dev/null +++ b/llvm/test/CodeGen/CHERI-Generic/RISCV64/memcpy-from-constant.ll @@ -0,0 +1,165 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes --force-update +; DO NOT EDIT -- This file was generated from test/CodeGen/CHERI-Generic/Inputs/memcpy-from-constant.ll +;; Copying from a zero constant can be converted to a memset (even with the tag preservation flags) +; RUN: llc -mtriple=riscv64 --relocation-model=pic -target-abi l64pc128d -mattr=+xcheri,+cap-mode,+f,+d < %s -o - | FileCheck %s + +@a = internal addrspace(200) constant ptr addrspace(200) null +@b = internal addrspace(200) constant ptr addrspace(200) null +@zero_constant = internal addrspace(200) constant [5 x ptr addrspace(200)] zeroinitializer +@constant_ptrs = internal addrspace(200) constant [2 x ptr addrspace(200)] [ptr addrspace(200) @a, ptr addrspace(200) @b] + +declare void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) noalias nocapture writeonly, ptr addrspace(200) noalias nocapture readonly, i64, i1 immarg) addrspace(200) #0 + +define linkonce_odr void @copy_from_zero_constant(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_zero_constant: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: csc cnull, 0(ca0) +; CHECK-NEXT: cret +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 16 %dst, ptr addrspace(200) align 16 @zero_constant, i64 16, i1 false) + ret void +} + +define linkonce_odr void @copy_from_zero_constant_with_offset(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_zero_constant_with_offset: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: csc cnull, 0(ca0) +; CHECK-NEXT: cret +do.body: + %src = getelementptr inbounds i8, ptr addrspace(200) @zero_constant, i64 16 + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 16 %dst, ptr addrspace(200) align 16 %src, i64 16, i1 false) + ret void +} + +define linkonce_odr void @copy_from_large_zero_constant(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_large_zero_constant: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: csd zero, 0(ca0) +; CHECK-NEXT: cret +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 16 %dst, ptr addrspace(200) align 16 @zero_constant, i64 8, i1 false) + ret void +} + +define linkonce_odr void @copy_from_ptr_constant(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_ptr_constant: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: .LBB3_1: # %do.body +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: auipcc ca1, %captab_pcrel_hi(constant_ptrs) +; CHECK-NEXT: clc ca1, %pcrel_lo(.LBB3_1)(ca1) +; CHECK-NEXT: clc ca1, 0(ca1) +; CHECK-NEXT: csc ca1, 0(ca0) +; CHECK-NEXT: cret +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 16 %dst, ptr addrspace(200) align 16 @constant_ptrs, i64 16, i1 false) + ret void +} + +define linkonce_odr void @copy_from_ptr_constant_with_offset(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_ptr_constant_with_offset: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: .LBB4_1: # %do.body +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: auipcc ca1, %captab_pcrel_hi(constant_ptrs) +; CHECK-NEXT: clc ca1, %pcrel_lo(.LBB4_1)(ca1) +; CHECK-NEXT: clc ca1, 16(ca1) +; CHECK-NEXT: csc ca1, 0(ca0) +; CHECK-NEXT: cret +do.body: + %src = getelementptr inbounds i8, ptr addrspace(200) @constant_ptrs, i64 16 + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 16 %dst, ptr addrspace(200) align 16 %src, i64 16, i1 false) + ret void +} + +;; Run the same tests again this time with must_preserve_tags to check that we don't call memcpy(). + +define linkonce_odr void @copy_from_zero_constant_preserve(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_zero_constant_preserve: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: csc cnull, 0(ca0) +; CHECK-NEXT: cret +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 16 %dst, ptr addrspace(200) align 16 @zero_constant, i64 16, i1 false) #1 + ret void +} + +define linkonce_odr void @copy_from_zero_constant_with_offset_preserve(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_zero_constant_with_offset_preserve: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: csc cnull, 0(ca0) +; CHECK-NEXT: cret +do.body: + %src = getelementptr inbounds i8, ptr addrspace(200) @zero_constant, i64 16 + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 16 %dst, ptr addrspace(200) align 16 %src, i64 16, i1 false) #1 + ret void +} + +define linkonce_odr void @copy_from_large_zero_constant_preserve(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_large_zero_constant_preserve: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: csd zero, 0(ca0) +; CHECK-NEXT: cret +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 16 %dst, ptr addrspace(200) align 16 @zero_constant, i64 8, i1 false) #1 + ret void +} + +define linkonce_odr void @copy_from_ptr_constant_preserve(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_ptr_constant_preserve: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: .LBB8_1: # %do.body +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: auipcc ca1, %captab_pcrel_hi(constant_ptrs) +; CHECK-NEXT: clc ca1, %pcrel_lo(.LBB8_1)(ca1) +; CHECK-NEXT: clc ca1, 0(ca1) +; CHECK-NEXT: csc ca1, 0(ca0) +; CHECK-NEXT: cret +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 16 %dst, ptr addrspace(200) align 16 @constant_ptrs, i64 16, i1 false) #1 + ret void +} + +define linkonce_odr void @copy_from_ptr_constant_with_offset_preserve(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_ptr_constant_with_offset_preserve: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: .LBB9_1: # %do.body +; CHECK-NEXT: # Label of block must be emitted +; CHECK-NEXT: auipcc ca1, %captab_pcrel_hi(constant_ptrs) +; CHECK-NEXT: clc ca1, %pcrel_lo(.LBB9_1)(ca1) +; CHECK-NEXT: clc ca1, 16(ca1) +; CHECK-NEXT: csc ca1, 0(ca0) +; CHECK-NEXT: cret +do.body: + %src = getelementptr inbounds i8, ptr addrspace(200) @constant_ptrs, i64 16 + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 16 %dst, ptr addrspace(200) align 16 %src, i64 16, i1 false) #1 + ret void +} + +;; Finally, check copying from a zero constant with insufficient known alignment. +;; We should be able to emit this inline since a zero constant source never has tags. + +define linkonce_odr void @copy_from_underaligned_zero_constant(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_underaligned_zero_constant: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: csd zero, 8(ca0) +; CHECK-NEXT: csd zero, 0(ca0) +; CHECK-NEXT: cret +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 8 %dst, ptr addrspace(200) align 8 @zero_constant, i64 16, i1 false) #1 + ret void +} + +define linkonce_odr void @copy_from_underaligned_zero_constant_preserve(ptr addrspace(200) %dst) addrspace(200) { +; CHECK-LABEL: copy_from_underaligned_zero_constant_preserve: +; CHECK: # %bb.0: # %do.body +; CHECK-NEXT: csd zero, 8(ca0) +; CHECK-NEXT: csd zero, 0(ca0) +; CHECK-NEXT: cret +do.body: + call void @llvm.memcpy.p200.p200.i64(ptr addrspace(200) align 8 %dst, ptr addrspace(200) align 8 @zero_constant, i64 16, i1 false) #1 + ret void +} + +attributes #0 = { argmemonly nocallback nofree nounwind willreturn } +attributes #1 = { must_preserve_cheri_tags "frontend-memtransfer-type"="'const UChar * __capability' (aka 'const char16_t * __capability')" }