From 85efaa09b4177a65d24d95ccacaefdab27b5be0f Mon Sep 17 00:00:00 2001 From: Alex Richardson Date: Mon, 17 Apr 2023 13:14:04 -0700 Subject: [PATCH 1/4] [InstSimplify] Add a test for current get/set intrinsic folding --- .../InstSimplify/cheri-intrinsics-get-set.ll | 296 ++++++++++++++++++ 1 file changed, 296 insertions(+) create mode 100644 llvm/test/Transforms/InstSimplify/cheri-intrinsics-get-set.ll diff --git a/llvm/test/Transforms/InstSimplify/cheri-intrinsics-get-set.ll b/llvm/test/Transforms/InstSimplify/cheri-intrinsics-get-set.ll new file mode 100644 index 000000000000..43d31dba2b31 --- /dev/null +++ b/llvm/test/Transforms/InstSimplify/cheri-intrinsics-get-set.ll @@ -0,0 +1,296 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes --force-update +;; Check folds for CHERI intrinsics that have both get and set variants +; RUN: sed 's/INTRINSIC/address/' %s | opt -S -passes=instsimplify | FileCheck %s --check-prefixes=ADDRESS +; RUN: sed 's/INTRINSIC/flags/' %s | opt -S -passes=instsimplify | FileCheck %s --check-prefixes=FLAGS +; RUN: sed 's/INTRINSIC/high/' %s | opt -S -passes=instsimplify | FileCheck %s --check-prefixes=HIGH +; RUN: sed 's/INTRINSIC/offset/' %s | opt -S -passes=instsimplify | FileCheck %s --check-prefixes=OFFSET +target datalayout = "pf200:128:128:128:64-A200-P200-G200" + +declare i64 @check_fold(i64) nounwind +declare i64 @llvm.cheri.cap.INTRINSIC.get.i64(i8 addrspace(200)*) nounwind +declare i8 addrspace(200)* @llvm.cheri.cap.INTRINSIC.set.i64(i8 addrspace(200)*, i64) nounwind + + +;; This is a no-op and should be folded to ret %arg +define i8 addrspace(200)* @fold_set_of_get(i8 addrspace(200)* %arg) nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@fold_set_of_get +; ADDRESS-SAME: (i8 addrspace(200)* [[ARG:%.*]]) addrspace(200) #[[ATTR0:[0-9]+]] { +; ADDRESS-NEXT: ret i8 addrspace(200)* [[ARG]] +; +; FLAGS-LABEL: define {{[^@]+}}@fold_set_of_get +; FLAGS-SAME: (i8 addrspace(200)* [[ARG:%.*]]) addrspace(200) #[[ATTR0:[0-9]+]] { +; FLAGS-NEXT: [[VALUE:%.*]] = tail call i64 @llvm.cheri.cap.flags.get.i64(i8 addrspace(200)* [[ARG]]) +; FLAGS-NEXT: [[RET:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.flags.set.i64(i8 addrspace(200)* [[ARG]], i64 [[VALUE]]) +; FLAGS-NEXT: ret i8 addrspace(200)* [[RET]] +; +; HIGH-LABEL: define {{[^@]+}}@fold_set_of_get +; HIGH-SAME: (i8 addrspace(200)* [[ARG:%.*]]) addrspace(200) #[[ATTR0:[0-9]+]] { +; HIGH-NEXT: [[VALUE:%.*]] = tail call i64 @llvm.cheri.cap.high.get.i64(i8 addrspace(200)* [[ARG]]) +; HIGH-NEXT: [[RET:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.high.set.i64(i8 addrspace(200)* [[ARG]], i64 [[VALUE]]) +; HIGH-NEXT: ret i8 addrspace(200)* [[RET]] +; +; OFFSET-LABEL: define {{[^@]+}}@fold_set_of_get +; OFFSET-SAME: (i8 addrspace(200)* [[ARG:%.*]]) addrspace(200) #[[ATTR0:[0-9]+]] { +; OFFSET-NEXT: ret i8 addrspace(200)* [[ARG]] +; + %value = tail call i64 @llvm.cheri.cap.INTRINSIC.get.i64(i8 addrspace(200)* %arg) + %ret = tail call i8 addrspace(200)* @llvm.cheri.cap.INTRINSIC.set.i64(i8 addrspace(200)* %arg, i64 %value) + ret i8 addrspace(200)* %ret +} + +;; negative test - get of a different value should not be folded to ret %arg +define i8 addrspace(200)* @fold_set_of_get_bad(i8 addrspace(200)* %arg, i8 addrspace(200)* %other) nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@fold_set_of_get_bad +; ADDRESS-SAME: (i8 addrspace(200)* [[ARG:%.*]], i8 addrspace(200)* [[OTHER:%.*]]) addrspace(200) #[[ATTR0]] { +; ADDRESS-NEXT: [[VALUE:%.*]] = tail call i64 @llvm.cheri.cap.address.get.i64(i8 addrspace(200)* [[OTHER]]) +; ADDRESS-NEXT: [[RET:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.address.set.i64(i8 addrspace(200)* [[ARG]], i64 [[VALUE]]) +; ADDRESS-NEXT: ret i8 addrspace(200)* [[RET]] +; +; FLAGS-LABEL: define {{[^@]+}}@fold_set_of_get_bad +; FLAGS-SAME: (i8 addrspace(200)* [[ARG:%.*]], i8 addrspace(200)* [[OTHER:%.*]]) addrspace(200) #[[ATTR0]] { +; FLAGS-NEXT: [[VALUE:%.*]] = tail call i64 @llvm.cheri.cap.flags.get.i64(i8 addrspace(200)* [[OTHER]]) +; FLAGS-NEXT: [[RET:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.flags.set.i64(i8 addrspace(200)* [[ARG]], i64 [[VALUE]]) +; FLAGS-NEXT: ret i8 addrspace(200)* [[RET]] +; +; HIGH-LABEL: define {{[^@]+}}@fold_set_of_get_bad +; HIGH-SAME: (i8 addrspace(200)* [[ARG:%.*]], i8 addrspace(200)* [[OTHER:%.*]]) addrspace(200) #[[ATTR0]] { +; HIGH-NEXT: [[VALUE:%.*]] = tail call i64 @llvm.cheri.cap.high.get.i64(i8 addrspace(200)* [[OTHER]]) +; HIGH-NEXT: [[RET:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.high.set.i64(i8 addrspace(200)* [[ARG]], i64 [[VALUE]]) +; HIGH-NEXT: ret i8 addrspace(200)* [[RET]] +; +; OFFSET-LABEL: define {{[^@]+}}@fold_set_of_get_bad +; OFFSET-SAME: (i8 addrspace(200)* [[ARG:%.*]], i8 addrspace(200)* [[OTHER:%.*]]) addrspace(200) #[[ATTR0]] { +; OFFSET-NEXT: [[VALUE:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(i8 addrspace(200)* [[OTHER]]) +; OFFSET-NEXT: [[RET:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.offset.set.i64(i8 addrspace(200)* [[ARG]], i64 [[VALUE]]) +; OFFSET-NEXT: ret i8 addrspace(200)* [[RET]] +; + %value = tail call i64 @llvm.cheri.cap.INTRINSIC.get.i64(i8 addrspace(200)* %other) + %ret = tail call i8 addrspace(200)* @llvm.cheri.cap.INTRINSIC.set.i64(i8 addrspace(200)* %arg, i64 %value) + ret i8 addrspace(200)* %ret +} + +;; For almost all intrinsics this get-of-set pair can be elided +;; NB: This is not true for flags where the value written by set could be truncated to another value +;; FIXME: high.get should be folded. +define i64 @fold_get_of_set(i8 addrspace(200)* %arg, i64 %value) nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@fold_get_of_set +; ADDRESS-SAME: (i8 addrspace(200)* [[ARG:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; ADDRESS-NEXT: ret i64 [[VALUE]] +; +; FLAGS-LABEL: define {{[^@]+}}@fold_get_of_set +; FLAGS-SAME: (i8 addrspace(200)* [[ARG:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; FLAGS-NEXT: [[MODIFIED:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.flags.set.i64(i8 addrspace(200)* [[ARG]], i64 [[VALUE]]) +; FLAGS-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.flags.get.i64(i8 addrspace(200)* [[MODIFIED]]) +; FLAGS-NEXT: ret i64 [[RET]] +; +; HIGH-LABEL: define {{[^@]+}}@fold_get_of_set +; HIGH-SAME: (i8 addrspace(200)* [[ARG:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; HIGH-NEXT: [[MODIFIED:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.high.set.i64(i8 addrspace(200)* [[ARG]], i64 [[VALUE]]) +; HIGH-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.high.get.i64(i8 addrspace(200)* [[MODIFIED]]) +; HIGH-NEXT: ret i64 [[RET]] +; +; OFFSET-LABEL: define {{[^@]+}}@fold_get_of_set +; OFFSET-SAME: (i8 addrspace(200)* [[ARG:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; OFFSET-NEXT: ret i64 [[VALUE]] +; + %modified = tail call i8 addrspace(200)* @llvm.cheri.cap.INTRINSIC.set.i64(i8 addrspace(200)* %arg, i64 %value) + %ret = tail call i64 @llvm.cheri.cap.INTRINSIC.get.i64(i8 addrspace(200)* %modified) + ret i64 %ret +} + +;; Negative test -- get is used on other intrinsic +define i64 @fold_get_of_set_bad(i8 addrspace(200)* %arg, i8 addrspace(200)* %other, i64 %value) nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@fold_get_of_set_bad +; ADDRESS-SAME: (i8 addrspace(200)* [[ARG:%.*]], i8 addrspace(200)* [[OTHER:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; ADDRESS-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.address.get.i64(i8 addrspace(200)* [[ARG]]) +; ADDRESS-NEXT: ret i64 [[RET]] +; +; FLAGS-LABEL: define {{[^@]+}}@fold_get_of_set_bad +; FLAGS-SAME: (i8 addrspace(200)* [[ARG:%.*]], i8 addrspace(200)* [[OTHER:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; FLAGS-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.flags.get.i64(i8 addrspace(200)* [[ARG]]) +; FLAGS-NEXT: ret i64 [[RET]] +; +; HIGH-LABEL: define {{[^@]+}}@fold_get_of_set_bad +; HIGH-SAME: (i8 addrspace(200)* [[ARG:%.*]], i8 addrspace(200)* [[OTHER:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; HIGH-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.high.get.i64(i8 addrspace(200)* [[ARG]]) +; HIGH-NEXT: ret i64 [[RET]] +; +; OFFSET-LABEL: define {{[^@]+}}@fold_get_of_set_bad +; OFFSET-SAME: (i8 addrspace(200)* [[ARG:%.*]], i8 addrspace(200)* [[OTHER:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; OFFSET-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(i8 addrspace(200)* [[ARG]]) +; OFFSET-NEXT: ret i64 [[RET]] +; + %modified = tail call i8 addrspace(200)* @llvm.cheri.cap.INTRINSIC.set.i64(i8 addrspace(200)* %other, i64 %value) + %ret = tail call i64 @llvm.cheri.cap.INTRINSIC.get.i64(i8 addrspace(200)* %arg) + ret i64 %ret +} + +define i64 @fold_get_on_null() nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@fold_get_on_null +; ADDRESS-SAME: () addrspace(200) #[[ATTR0]] { +; ADDRESS-NEXT: ret i64 0 +; +; FLAGS-LABEL: define {{[^@]+}}@fold_get_on_null +; FLAGS-SAME: () addrspace(200) #[[ATTR0]] { +; FLAGS-NEXT: ret i64 0 +; +; HIGH-LABEL: define {{[^@]+}}@fold_get_on_null +; HIGH-SAME: () addrspace(200) #[[ATTR0]] { +; HIGH-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.high.get.i64(i8 addrspace(200)* null) +; HIGH-NEXT: ret i64 [[RET]] +; +; OFFSET-LABEL: define {{[^@]+}}@fold_get_on_null +; OFFSET-SAME: () addrspace(200) #[[ATTR0]] { +; OFFSET-NEXT: ret i64 0 +; + %ret = tail call i64 @llvm.cheri.cap.INTRINSIC.get.i64(i8 addrspace(200)* null) + ret i64 %ret +} + +define i64 @fold_get_on_null_with_gep(i64 %value, i64 %gepoff) nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@fold_get_on_null_with_gep +; ADDRESS-SAME: (i64 [[VALUE:%.*]], i64 [[GEPOFF:%.*]]) addrspace(200) #[[ATTR0]] { +; ADDRESS-NEXT: ret i64 [[GEPOFF]] +; +; FLAGS-LABEL: define {{[^@]+}}@fold_get_on_null_with_gep +; FLAGS-SAME: (i64 [[VALUE:%.*]], i64 [[GEPOFF:%.*]]) addrspace(200) #[[ATTR0]] { +; FLAGS-NEXT: ret i64 0 +; +; HIGH-LABEL: define {{[^@]+}}@fold_get_on_null_with_gep +; HIGH-SAME: (i64 [[VALUE:%.*]], i64 [[GEPOFF:%.*]]) addrspace(200) #[[ATTR0]] { +; HIGH-NEXT: [[TMP:%.*]] = getelementptr i8, i8 addrspace(200)* null, i64 [[GEPOFF]] +; HIGH-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.high.get.i64(i8 addrspace(200)* [[TMP]]) +; HIGH-NEXT: ret i64 [[RET]] +; +; OFFSET-LABEL: define {{[^@]+}}@fold_get_on_null_with_gep +; OFFSET-SAME: (i64 [[VALUE:%.*]], i64 [[GEPOFF:%.*]]) addrspace(200) #[[ATTR0]] { +; OFFSET-NEXT: ret i64 [[GEPOFF]] +; + %tmp = getelementptr i8, i8 addrspace(200)* null, i64 %gepoff + %ret = tail call i64 @llvm.cheri.cap.INTRINSIC.get.i64(i8 addrspace(200)* %tmp) + ret i64 %ret +} + +;; TODO: Setting is idempotent, so the first call should be elided +define i8 addrspace(200)* @set_idempotent(i8 addrspace(200)* %arg, i64 %value) nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@set_idempotent +; ADDRESS-SAME: (i8 addrspace(200)* [[ARG:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; ADDRESS-NEXT: [[MODIFIED:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.address.set.i64(i8 addrspace(200)* [[ARG]], i64 [[VALUE]]) +; ADDRESS-NEXT: [[RET:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.address.set.i64(i8 addrspace(200)* [[MODIFIED]], i64 [[VALUE]]) +; ADDRESS-NEXT: ret i8 addrspace(200)* [[RET]] +; +; FLAGS-LABEL: define {{[^@]+}}@set_idempotent +; FLAGS-SAME: (i8 addrspace(200)* [[ARG:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; FLAGS-NEXT: [[MODIFIED:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.flags.set.i64(i8 addrspace(200)* [[ARG]], i64 [[VALUE]]) +; FLAGS-NEXT: [[RET:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.flags.set.i64(i8 addrspace(200)* [[MODIFIED]], i64 [[VALUE]]) +; FLAGS-NEXT: ret i8 addrspace(200)* [[RET]] +; +; HIGH-LABEL: define {{[^@]+}}@set_idempotent +; HIGH-SAME: (i8 addrspace(200)* [[ARG:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; HIGH-NEXT: [[MODIFIED:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.high.set.i64(i8 addrspace(200)* [[ARG]], i64 [[VALUE]]) +; HIGH-NEXT: [[RET:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.high.set.i64(i8 addrspace(200)* [[MODIFIED]], i64 [[VALUE]]) +; HIGH-NEXT: ret i8 addrspace(200)* [[RET]] +; +; OFFSET-LABEL: define {{[^@]+}}@set_idempotent +; OFFSET-SAME: (i8 addrspace(200)* [[ARG:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; OFFSET-NEXT: [[MODIFIED:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.offset.set.i64(i8 addrspace(200)* [[ARG]], i64 [[VALUE]]) +; OFFSET-NEXT: [[RET:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.offset.set.i64(i8 addrspace(200)* [[MODIFIED]], i64 [[VALUE]]) +; OFFSET-NEXT: ret i8 addrspace(200)* [[RET]] +; + %modified = tail call i8 addrspace(200)* @llvm.cheri.cap.INTRINSIC.set.i64(i8 addrspace(200)* %arg, i64 %value) + %ret = tail call i8 addrspace(200)* @llvm.cheri.cap.INTRINSIC.set.i64(i8 addrspace(200)* %modified, i64 %value) + ret i8 addrspace(200)* %ret +} + + +;; Negative test - calls with the same base argument are not yet elided if the +;; second argument is different since they could have side-effects such as +;; clearing the tag. For example `setoffset(setoffset(x, ), )` +;; would result in an untagged value if the first setoffset went outside the +;; representable range but that detagging would not happen if we elide it. +;; TODO: Maybe we should do this fold since people should not be relying on +;; representability to clear tags of capabilities. +define i8 addrspace(200)* @set_different_values(i8 addrspace(200)* %arg, i64 %value, i64 %value2) nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@set_different_values +; ADDRESS-SAME: (i8 addrspace(200)* [[ARG:%.*]], i64 [[VALUE:%.*]], i64 [[VALUE2:%.*]]) addrspace(200) #[[ATTR0]] { +; ADDRESS-NEXT: [[MODIFIED:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.address.set.i64(i8 addrspace(200)* [[ARG]], i64 [[VALUE]]) +; ADDRESS-NEXT: [[RET:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.address.set.i64(i8 addrspace(200)* [[MODIFIED]], i64 [[VALUE]]) +; ADDRESS-NEXT: ret i8 addrspace(200)* [[RET]] +; +; FLAGS-LABEL: define {{[^@]+}}@set_different_values +; FLAGS-SAME: (i8 addrspace(200)* [[ARG:%.*]], i64 [[VALUE:%.*]], i64 [[VALUE2:%.*]]) addrspace(200) #[[ATTR0]] { +; FLAGS-NEXT: [[MODIFIED:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.flags.set.i64(i8 addrspace(200)* [[ARG]], i64 [[VALUE]]) +; FLAGS-NEXT: [[RET:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.flags.set.i64(i8 addrspace(200)* [[MODIFIED]], i64 [[VALUE]]) +; FLAGS-NEXT: ret i8 addrspace(200)* [[RET]] +; +; HIGH-LABEL: define {{[^@]+}}@set_different_values +; HIGH-SAME: (i8 addrspace(200)* [[ARG:%.*]], i64 [[VALUE:%.*]], i64 [[VALUE2:%.*]]) addrspace(200) #[[ATTR0]] { +; HIGH-NEXT: [[MODIFIED:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.high.set.i64(i8 addrspace(200)* [[ARG]], i64 [[VALUE]]) +; HIGH-NEXT: [[RET:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.high.set.i64(i8 addrspace(200)* [[MODIFIED]], i64 [[VALUE]]) +; HIGH-NEXT: ret i8 addrspace(200)* [[RET]] +; +; OFFSET-LABEL: define {{[^@]+}}@set_different_values +; OFFSET-SAME: (i8 addrspace(200)* [[ARG:%.*]], i64 [[VALUE:%.*]], i64 [[VALUE2:%.*]]) addrspace(200) #[[ATTR0]] { +; OFFSET-NEXT: [[MODIFIED:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.offset.set.i64(i8 addrspace(200)* [[ARG]], i64 [[VALUE]]) +; OFFSET-NEXT: [[RET:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.offset.set.i64(i8 addrspace(200)* [[MODIFIED]], i64 [[VALUE]]) +; OFFSET-NEXT: ret i8 addrspace(200)* [[RET]] +; + %modified = tail call i8 addrspace(200)* @llvm.cheri.cap.INTRINSIC.set.i64(i8 addrspace(200)* %arg, i64 %value) + %ret = tail call i8 addrspace(200)* @llvm.cheri.cap.INTRINSIC.set.i64(i8 addrspace(200)* %modified, i64 %value) + ret i8 addrspace(200)* %ret +} + +define i8 addrspace(200)* @fold_set_on_null(i64 %value) nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@fold_set_on_null +; ADDRESS-SAME: (i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; ADDRESS-NEXT: [[RET:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.address.set.i64(i8 addrspace(200)* null, i64 [[VALUE]]) +; ADDRESS-NEXT: ret i8 addrspace(200)* [[RET]] +; +; FLAGS-LABEL: define {{[^@]+}}@fold_set_on_null +; FLAGS-SAME: (i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; FLAGS-NEXT: [[RET:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.flags.set.i64(i8 addrspace(200)* null, i64 [[VALUE]]) +; FLAGS-NEXT: ret i8 addrspace(200)* [[RET]] +; +; HIGH-LABEL: define {{[^@]+}}@fold_set_on_null +; HIGH-SAME: (i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; HIGH-NEXT: [[RET:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.high.set.i64(i8 addrspace(200)* null, i64 [[VALUE]]) +; HIGH-NEXT: ret i8 addrspace(200)* [[RET]] +; +; OFFSET-LABEL: define {{[^@]+}}@fold_set_on_null +; OFFSET-SAME: (i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; OFFSET-NEXT: [[RET:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.offset.set.i64(i8 addrspace(200)* null, i64 [[VALUE]]) +; OFFSET-NEXT: ret i8 addrspace(200)* [[RET]] +; + %ret = tail call i8 addrspace(200)* @llvm.cheri.cap.INTRINSIC.set.i64(i8 addrspace(200)* null, i64 %value) + ret i8 addrspace(200)* %ret +} + +define i8 addrspace(200)* @fold_set_on_null_with_gep(i64 %value, i64 %gepoff) nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@fold_set_on_null_with_gep +; ADDRESS-SAME: (i64 [[VALUE:%.*]], i64 [[GEPOFF:%.*]]) addrspace(200) #[[ATTR0]] { +; ADDRESS-NEXT: [[TMP:%.*]] = getelementptr i8, i8 addrspace(200)* null, i64 [[GEPOFF]] +; ADDRESS-NEXT: [[RET:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.address.set.i64(i8 addrspace(200)* [[TMP]], i64 [[VALUE]]) +; ADDRESS-NEXT: ret i8 addrspace(200)* [[RET]] +; +; FLAGS-LABEL: define {{[^@]+}}@fold_set_on_null_with_gep +; FLAGS-SAME: (i64 [[VALUE:%.*]], i64 [[GEPOFF:%.*]]) addrspace(200) #[[ATTR0]] { +; FLAGS-NEXT: [[TMP:%.*]] = getelementptr i8, i8 addrspace(200)* null, i64 [[GEPOFF]] +; FLAGS-NEXT: [[RET:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.flags.set.i64(i8 addrspace(200)* [[TMP]], i64 [[VALUE]]) +; FLAGS-NEXT: ret i8 addrspace(200)* [[RET]] +; +; HIGH-LABEL: define {{[^@]+}}@fold_set_on_null_with_gep +; HIGH-SAME: (i64 [[VALUE:%.*]], i64 [[GEPOFF:%.*]]) addrspace(200) #[[ATTR0]] { +; HIGH-NEXT: [[TMP:%.*]] = getelementptr i8, i8 addrspace(200)* null, i64 [[GEPOFF]] +; HIGH-NEXT: [[RET:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.high.set.i64(i8 addrspace(200)* [[TMP]], i64 [[VALUE]]) +; HIGH-NEXT: ret i8 addrspace(200)* [[RET]] +; +; OFFSET-LABEL: define {{[^@]+}}@fold_set_on_null_with_gep +; OFFSET-SAME: (i64 [[VALUE:%.*]], i64 [[GEPOFF:%.*]]) addrspace(200) #[[ATTR0]] { +; OFFSET-NEXT: [[TMP:%.*]] = getelementptr i8, i8 addrspace(200)* null, i64 [[GEPOFF]] +; OFFSET-NEXT: [[RET:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.offset.set.i64(i8 addrspace(200)* [[TMP]], i64 [[VALUE]]) +; OFFSET-NEXT: ret i8 addrspace(200)* [[RET]] +; + %tmp = getelementptr i8, i8 addrspace(200)* null, i64 %gepoff + %ret = tail call i8 addrspace(200)* @llvm.cheri.cap.INTRINSIC.set.i64(i8 addrspace(200)* %tmp, i64 %value) + ret i8 addrspace(200)* %ret +} From 61a3130bab7ac861e2164b667770d7086618e009 Mon Sep 17 00:00:00 2001 From: Alex Richardson Date: Fri, 12 May 2023 11:01:13 -0700 Subject: [PATCH 2/4] [InstSimplify] Simplify getoffset/getaddr folding code. NFC Now that we depend on C++14, we can use switches in constexpr functions and no longer need to pass two template parameters. --- llvm/include/llvm/IR/CheriIntrinsics.h | 6 ++++-- llvm/lib/Analysis/InstructionSimplify.cpp | 20 ++++---------------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/llvm/include/llvm/IR/CheriIntrinsics.h b/llvm/include/llvm/IR/CheriIntrinsics.h index caa292981a72..7edd61df9d0e 100644 --- a/llvm/include/llvm/IR/CheriIntrinsics.h +++ b/llvm/include/llvm/IR/CheriIntrinsics.h @@ -21,7 +21,8 @@ namespace llvm { namespace cheri { -inline Intrinsic::ID correspondingSetIntrinsic(Intrinsic::ID GetIntrin) { +inline constexpr Intrinsic::ID +correspondingSetIntrinsic(Intrinsic::ID GetIntrin) { switch (GetIntrin) { case Intrinsic::cheri_cap_offset_get: return Intrinsic::cheri_cap_offset_set; @@ -33,7 +34,8 @@ inline Intrinsic::ID correspondingSetIntrinsic(Intrinsic::ID GetIntrin) { llvm_unreachable("No matching set intrinsic"); } } -inline Intrinsic::ID correspondingGetIntrinsic(Intrinsic::ID SetIntrin) { +inline constexpr Intrinsic::ID +correspondingGetIntrinsic(Intrinsic::ID SetIntrin) { switch (SetIntrin) { case Intrinsic::cheri_cap_offset_set: return Intrinsic::cheri_cap_offset_get; diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp index 577316b1a4b7..2255980d2456 100644 --- a/llvm/lib/Analysis/InstructionSimplify.cpp +++ b/llvm/lib/Analysis/InstructionSimplify.cpp @@ -5774,20 +5774,6 @@ static Value *inferCapabilityOffsetOrAddr(Value *V, Type *ResultTy, return nullptr; } -Value *simplifyCapabilityGetOffset(Value *V, Type *ResultTy, - const DataLayout &DL) { - return inferCapabilityOffsetOrAddr( - V, ResultTy, DL); -} - -Value *simplifyCapabilityGetAddress(Value *V, Type *ResultTy, - const DataLayout &DL) { - return inferCapabilityOffsetOrAddr( - V, ResultTy, DL); -} - static Value *simplifyUnaryIntrinsic(Function *F, Value *Op0, const SimplifyQuery &Q) { // Idempotent functions return the same result when called repeatedly. @@ -5852,11 +5838,13 @@ static Value *simplifyUnaryIntrinsic(Function *F, Value *Op0, case Intrinsic::cheri_cap_length_get: break; case Intrinsic::cheri_cap_address_get: - if (Value *V = simplifyCapabilityGetAddress(Op0, F->getReturnType(), Q.DL)) + if (auto *V = inferCapabilityOffsetOrAddr( + Op0, F->getReturnType(), Q.DL)) return V; break; case Intrinsic::cheri_cap_offset_get: - if (Value *V = simplifyCapabilityGetOffset(Op0, F->getReturnType(), Q.DL)) + if (auto *V = inferCapabilityOffsetOrAddr( + Op0, F->getReturnType(), Q.DL)) return V; break; From fcc87ceee10a99e302670f2e2eb0e7261cc905ac Mon Sep 17 00:00:00 2001 From: Alex Richardson Date: Fri, 12 May 2023 11:39:01 -0700 Subject: [PATCH 3/4] [InstSimplify] Add folds for @llvm.cheri.cap.high.get As part of this refactor some of the CHERI get intrinsics folding code. This refactoring introduces a minor change to getoffset folding as well: we no longer fold arbitrary getoffset(setoffset(A, B)) -> B since the setoffset could have changed the bounds interpretation and therefore also the result. This optimization is unlikely to make a difference in practise and is somewhat incorrect so I believe dropping it makes sense. --- clang/test/CodeGen/cheri/vaddr-mode.c | 12 +++- llvm/include/llvm/IR/CheriIntrinsics.h | 4 ++ llvm/lib/Analysis/InstructionSimplify.cpp | 56 +++++++++++++++++-- ...insics-folding-broken-module-regression.ll | 8 +-- ...insics-folding-broken-module-regression.ll | 8 +-- ...insics-folding-broken-module-regression.ll | 16 +++--- .../InstSimplify/cheri-intrinsics-get-set.ll | 17 +++--- 7 files changed, 87 insertions(+), 34 deletions(-) diff --git a/clang/test/CodeGen/cheri/vaddr-mode.c b/clang/test/CodeGen/cheri/vaddr-mode.c index 9293f60a2e20..8fb00a6cac96 100644 --- a/clang/test/CodeGen/cheri/vaddr-mode.c +++ b/clang/test/CodeGen/cheri/vaddr-mode.c @@ -48,7 +48,9 @@ long write_uintcap(__uintcap_t *cap) { // OFFSET-NEXT: entry: // OFFSET-NEXT: [[TMP0:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(ptr addrspace(200) [[CAP:%.*]]) // OFFSET-NEXT: [[AND:%.*]] = and i64 [[TMP0]], 3 -// OFFSET-NEXT: ret i64 [[AND]] +// OFFSET-NEXT: [[TMP1:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.offset.set.i64(i8 addrspace(200)* [[CAP]], i64 [[AND]]) +// OFFSET-NEXT: [[TMP2:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(i8 addrspace(200)* [[TMP1]]) +// OFFSET-NEXT: ret i64 [[TMP2]] // long get_low_bits(__uintcap_t cap) { return cap & 3; @@ -126,7 +128,9 @@ __uintcap_t xor_uintcap(__uintcap_t cap, __uintcap_t cap2) { // OFFSET-NEXT: [[TMP0:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(ptr addrspace(200) [[CAP:%.*]]) // OFFSET-NEXT: [[TMP1:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(ptr addrspace(200) [[CAP2:%.*]]) // OFFSET-NEXT: [[XOR:%.*]] = xor i64 [[TMP1]], [[TMP0]] -// OFFSET-NEXT: ret i64 [[XOR]] +// OFFSET-NEXT: [[TMP2:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.offset.set.i64(i8 addrspace(200)* [[CAP]], i64 [[XOR]]) +// OFFSET-NEXT: [[TMP3:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(i8 addrspace(200)* [[TMP2]]) +// OFFSET-NEXT: ret i64 [[TMP3]] // long xor_uintcap_return_long(__uintcap_t cap, __uintcap_t cap2) { return cap ^ cap2; @@ -160,7 +164,9 @@ __uintcap_t modulo_return_uintcap(__uintcap_t cap) { // OFFSET-NEXT: entry: // OFFSET-NEXT: [[TMP0:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(ptr addrspace(200) [[CAP:%.*]]) // OFFSET-NEXT: [[REM:%.*]] = and i64 [[TMP0]], 31 -// OFFSET-NEXT: ret i64 [[REM]] +// OFFSET-NEXT: [[TMP1:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.offset.set.i64(i8 addrspace(200)* [[CAP]], i64 [[REM]]) +// OFFSET-NEXT: [[TMP2:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(i8 addrspace(200)* [[TMP1]]) +// OFFSET-NEXT: ret i64 [[TMP2]] // long modulo_return_long(__uintcap_t cap) { return cap % 32; diff --git a/llvm/include/llvm/IR/CheriIntrinsics.h b/llvm/include/llvm/IR/CheriIntrinsics.h index 7edd61df9d0e..06f776f1af26 100644 --- a/llvm/include/llvm/IR/CheriIntrinsics.h +++ b/llvm/include/llvm/IR/CheriIntrinsics.h @@ -30,6 +30,8 @@ correspondingSetIntrinsic(Intrinsic::ID GetIntrin) { return Intrinsic::cheri_cap_address_set; case Intrinsic::cheri_cap_flags_get: return Intrinsic::cheri_cap_flags_set; + case Intrinsic::cheri_cap_high_get: + return Intrinsic::cheri_cap_high_set; default: llvm_unreachable("No matching set intrinsic"); } @@ -43,6 +45,8 @@ correspondingGetIntrinsic(Intrinsic::ID SetIntrin) { return Intrinsic::cheri_cap_address_get; case Intrinsic::cheri_cap_flags_set: return Intrinsic::cheri_cap_flags_get; + case Intrinsic::cheri_cap_high_set: + return Intrinsic::cheri_cap_high_get; default: llvm_unreachable("No matching get intrinsic"); } diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp index 2255980d2456..38a7e53f9498 100644 --- a/llvm/lib/Analysis/InstructionSimplify.cpp +++ b/llvm/lib/Analysis/InstructionSimplify.cpp @@ -5716,15 +5716,47 @@ stripAndAccumulateGEPsAndPointerCastsSameRepr(Value *V, const DataLayout &DL, return Result; } -template +// Common helper to fold llvm.cheri.get.foo intrinsics +template +static Value *foldCheriGetSetPair(Value *V, Type *ResultTy, + const DataLayout &DL) { + constexpr Intrinsic::ID SetIntrin = cheri::correspondingSetIntrinsic(Intrin); + // getFoo(setFoo(A, B)) -> B + // This is true for address and high, but not necessarily true for flags and + // offset since setflags truncates the value and setoffset may change the + // bounds representation (and thus the offset) if it is significantly OOB. + constexpr bool SetUpdatesAllGetBits = + SetIntrin != Intrinsic::cheri_cap_offset_set && + SetIntrin != Intrinsic::cheri_cap_flags_set; + Value *IntrinArg = nullptr; + if (SetUpdatesAllGetBits && + match(V, m_Intrinsic(m_Value(), m_Value(IntrinArg)))) { + return IntrinArg; + } + // All intrinsics we call this function for return zero for NULL inputs. + // In fact, we can extend this to any input derived from NULL where the only + // operations that have been address manipulations (unless we are retrieving + // a result depending on the address), since those cannot affect non-bounds + // capability metadata. + constexpr bool CanIgnoreAddressManipulation = + Intrin != Intrinsic::cheri_cap_address_get && + Intrin != Intrinsic::cheri_cap_offset_get; + Value *Base = CanIgnoreAddressManipulation + ? getBasePtrIgnoringCapabilityAddressManipulation(V, DL) + : V->stripPointerCastsSameRepresentation(); + if (isa(Base)) { + return Constant::getNullValue(ResultTy); + } + return nullptr; +} + +template static Value *inferCapabilityOffsetOrAddr(Value *V, Type *ResultTy, const DataLayout &DL) { + if (Value *Ret = foldCheriGetSetPair(V, ResultTy, DL)) + return Ret; // Try to infer the offset/address from a prior setoffset/setaddr value Value *IntrinArg = nullptr; - // getaddr(setaddr(A, B)) -> B and getoffset(setoffset(A, B)) -> B - if (match(V, m_Intrinsic(m_Value(), m_Value(IntrinArg)))) { - return IntrinArg; - } // get{addr,offset}(setaddr(NULL, B)) -> B if (match(V, m_Intrinsic( m_Zero(), m_Value(IntrinArg)))) { @@ -5765,6 +5797,7 @@ static Value *inferCapabilityOffsetOrAddr(Value *V, Type *ResultTy, // We can also fold chains of constant GEPS: // For example: getoffset(GEP(setoffset(A, Const1), 100) -> Const1 + 100 ConstantInt *ConstSetArg; + constexpr Intrinsic::ID SetIntrin = cheri::correspondingSetIntrinsic(Intrin); if (match(BasePtr, m_Intrinsic(m_Value(), m_ConstantInt(ConstSetArg)))) { return ConstantInt::get(ResultTy, ConstSetArg->getValue() + OffsetAPInt); @@ -5804,7 +5837,6 @@ static Value *simplifyUnaryIntrinsic(Function *F, Value *Op0, return Constant::getNullValue(F->getReturnType()); break; case Intrinsic::cheri_cap_sealed_get: - case Intrinsic::cheri_cap_flags_get: case Intrinsic::cheri_cap_base_get: // Values derived from NULL and where the only operations that have been // applied are address manipulations, always have the following properties: @@ -5842,6 +5874,18 @@ static Value *simplifyUnaryIntrinsic(Function *F, Value *Op0, Op0, F->getReturnType(), Q.DL)) return V; break; + case Intrinsic::cheri_cap_flags_get: { + if (auto *Ret = foldCheriGetSetPair( + Op0, F->getReturnType(), Q.DL)) + return Ret; + break; + } + case Intrinsic::cheri_cap_high_get: { + if (auto *Ret = foldCheriGetSetPair( + Op0, F->getReturnType(), Q.DL)) + return Ret; + break; + } case Intrinsic::cheri_cap_offset_get: if (auto *V = inferCapabilityOffsetOrAddr( Op0, F->getReturnType(), Q.DL)) diff --git a/llvm/test/CodeGen/CHERI-Generic/MIPS/cheri-intrinsics-folding-broken-module-regression.ll b/llvm/test/CodeGen/CHERI-Generic/MIPS/cheri-intrinsics-folding-broken-module-regression.ll index eb9b29364d1c..e384bdf8d790 100644 --- a/llvm/test/CodeGen/CHERI-Generic/MIPS/cheri-intrinsics-folding-broken-module-regression.ll +++ b/llvm/test/CodeGen/CHERI-Generic/MIPS/cheri-intrinsics-folding-broken-module-regression.ll @@ -23,20 +23,20 @@ define void @g(i64 %x, i64 %y) addrspace(200) nounwind { ; ASM-NEXT: lui $1, %pcrel_hi(_CHERI_CAPABILITY_TABLE_-8) ; ASM-NEXT: daddiu $1, $1, %pcrel_lo(_CHERI_CAPABILITY_TABLE_-4) ; ASM-NEXT: cgetpccincoffset $c1, $1 -; ASM-NEXT: daddu $2, $5, $4 ; ASM-NEXT: clcbi $c2, %captab20(d)($c1) ; ASM-NEXT: clcbi $c1, %captab20(e)($c1) ; ASM-NEXT: cgetoffset $1, $c2 -; ASM-NEXT: daddu $1, $2, $1 +; ASM-NEXT: daddu $1, $1, $4 ; ASM-NEXT: csetoffset $c2, $c2, $1 +; ASM-NEXT: cincoffset $c2, $c2, $5 ; ASM-NEXT: cjr $c17 ; ASM-NEXT: csc $c2, $zero, 0($c1) ; CHECK-LABEL: define {{[^@]+}}@g ; CHECK-SAME: (i64 [[X:%.*]], i64 [[Y:%.*]]) addrspace(200) #[[ATTR0:[0-9]+]] { ; CHECK-NEXT: [[TMP3:%.*]] = call i64 @llvm.cheri.cap.offset.get.i64(i8 addrspace(200)* bitcast (i64 addrspace(200)* @d to i8 addrspace(200)*)) ; CHECK-NEXT: [[ADD:%.*]] = add i64 [[TMP3]], [[X]] -; CHECK-NEXT: [[ADD1:%.*]] = add i64 [[ADD]], [[Y]] -; CHECK-NEXT: [[TMP11:%.*]] = call i8 addrspace(200)* @llvm.cheri.cap.offset.set.i64(i8 addrspace(200)* bitcast (i64 addrspace(200)* @d to i8 addrspace(200)*), i64 [[ADD1]]) +; CHECK-NEXT: [[TMP5:%.*]] = call i8 addrspace(200)* @llvm.cheri.cap.offset.set.i64(i8 addrspace(200)* bitcast (i64 addrspace(200)* @d to i8 addrspace(200)*), i64 [[ADD]]) +; CHECK-NEXT: [[TMP11:%.*]] = getelementptr i8, i8 addrspace(200)* [[TMP5]], i64 [[Y]] ; CHECK-NEXT: store i8 addrspace(200)* [[TMP11]], i8 addrspace(200)* addrspace(200)* @e, align 32 ; CHECK-NEXT: ret void ; diff --git a/llvm/test/CodeGen/CHERI-Generic/RISCV32/cheri-intrinsics-folding-broken-module-regression.ll b/llvm/test/CodeGen/CHERI-Generic/RISCV32/cheri-intrinsics-folding-broken-module-regression.ll index ec4a4d9f8bbb..7c2bf8323630 100644 --- a/llvm/test/CodeGen/CHERI-Generic/RISCV32/cheri-intrinsics-folding-broken-module-regression.ll +++ b/llvm/test/CodeGen/CHERI-Generic/RISCV32/cheri-intrinsics-folding-broken-module-regression.ll @@ -27,17 +27,17 @@ define void @g(i32 %x, i32 %y) addrspace(200) nounwind { ; ASM-NEXT: .LBB0_2: # Label of block must be emitted ; ASM-NEXT: auipcc ca4, %captab_pcrel_hi(e) ; ASM-NEXT: clc ca4, %pcrel_lo(.LBB0_2)(ca4) -; ASM-NEXT: add a0, a1, a0 -; ASM-NEXT: add a0, a0, a3 +; ASM-NEXT: add a0, a3, a0 ; ASM-NEXT: csetoffset ca0, ca2, a0 +; ASM-NEXT: cincoffset ca0, ca0, a1 ; ASM-NEXT: csc ca0, 0(ca4) ; ASM-NEXT: cret ; CHECK-LABEL: define {{[^@]+}}@g ; CHECK-SAME: (i32 [[X:%.*]], i32 [[Y:%.*]]) addrspace(200) #[[ATTR0:[0-9]+]] { ; CHECK-NEXT: [[TMP3:%.*]] = call i32 @llvm.cheri.cap.offset.get.i32(i8 addrspace(200)* bitcast (i32 addrspace(200)* @d to i8 addrspace(200)*)) ; CHECK-NEXT: [[ADD:%.*]] = add i32 [[TMP3]], [[X]] -; CHECK-NEXT: [[ADD1:%.*]] = add i32 [[ADD]], [[Y]] -; CHECK-NEXT: [[TMP11:%.*]] = call i8 addrspace(200)* @llvm.cheri.cap.offset.set.i32(i8 addrspace(200)* bitcast (i32 addrspace(200)* @d to i8 addrspace(200)*), i32 [[ADD1]]) +; CHECK-NEXT: [[TMP5:%.*]] = call i8 addrspace(200)* @llvm.cheri.cap.offset.set.i32(i8 addrspace(200)* bitcast (i32 addrspace(200)* @d to i8 addrspace(200)*), i32 [[ADD]]) +; CHECK-NEXT: [[TMP11:%.*]] = getelementptr i8, i8 addrspace(200)* [[TMP5]], i32 [[Y]] ; CHECK-NEXT: store i8 addrspace(200)* [[TMP11]], i8 addrspace(200)* addrspace(200)* @e, align 32 ; CHECK-NEXT: ret void ; diff --git a/llvm/test/CodeGen/CHERI-Generic/RISCV64/cheri-intrinsics-folding-broken-module-regression.ll b/llvm/test/CodeGen/CHERI-Generic/RISCV64/cheri-intrinsics-folding-broken-module-regression.ll index 1cfe9f28bb12..aa3cb55228c0 100644 --- a/llvm/test/CodeGen/CHERI-Generic/RISCV64/cheri-intrinsics-folding-broken-module-regression.ll +++ b/llvm/test/CodeGen/CHERI-Generic/RISCV64/cheri-intrinsics-folding-broken-module-regression.ll @@ -23,21 +23,21 @@ define void @g(i64 %x, i64 %y) addrspace(200) nounwind { ; ASM-NEXT: .LBB0_1: # Label of block must be emitted ; ASM-NEXT: auipcc ca2, %captab_pcrel_hi(d) ; ASM-NEXT: clc ca2, %pcrel_lo(.LBB0_1)(ca2) -; ASM-NEXT: add a0, a1, a0 -; ASM-NEXT: .LBB0_2: # Label of block must be emitted -; ASM-NEXT: auipcc ca1, %captab_pcrel_hi(e) -; ASM-NEXT: clc ca1, %pcrel_lo(.LBB0_2)(ca1) ; ASM-NEXT: cgetoffset a3, ca2 -; ASM-NEXT: add a0, a0, a3 +; ASM-NEXT: .LBB0_2: # Label of block must be emitted +; ASM-NEXT: auipcc ca4, %captab_pcrel_hi(e) +; ASM-NEXT: clc ca4, %pcrel_lo(.LBB0_2)(ca4) +; ASM-NEXT: add a0, a3, a0 ; ASM-NEXT: csetoffset ca0, ca2, a0 -; ASM-NEXT: csc ca0, 0(ca1) +; ASM-NEXT: cincoffset ca0, ca0, a1 +; ASM-NEXT: csc ca0, 0(ca4) ; ASM-NEXT: cret ; CHECK-LABEL: define {{[^@]+}}@g ; CHECK-SAME: (i64 [[X:%.*]], i64 [[Y:%.*]]) addrspace(200) #[[ATTR0:[0-9]+]] { ; CHECK-NEXT: [[TMP3:%.*]] = call i64 @llvm.cheri.cap.offset.get.i64(i8 addrspace(200)* bitcast (i64 addrspace(200)* @d to i8 addrspace(200)*)) ; CHECK-NEXT: [[ADD:%.*]] = add i64 [[TMP3]], [[X]] -; CHECK-NEXT: [[ADD1:%.*]] = add i64 [[ADD]], [[Y]] -; CHECK-NEXT: [[TMP11:%.*]] = call i8 addrspace(200)* @llvm.cheri.cap.offset.set.i64(i8 addrspace(200)* bitcast (i64 addrspace(200)* @d to i8 addrspace(200)*), i64 [[ADD1]]) +; CHECK-NEXT: [[TMP5:%.*]] = call i8 addrspace(200)* @llvm.cheri.cap.offset.set.i64(i8 addrspace(200)* bitcast (i64 addrspace(200)* @d to i8 addrspace(200)*), i64 [[ADD]]) +; CHECK-NEXT: [[TMP11:%.*]] = getelementptr i8, i8 addrspace(200)* [[TMP5]], i64 [[Y]] ; CHECK-NEXT: store i8 addrspace(200)* [[TMP11]], i8 addrspace(200)* addrspace(200)* @e, align 32 ; CHECK-NEXT: ret void ; diff --git a/llvm/test/Transforms/InstSimplify/cheri-intrinsics-get-set.ll b/llvm/test/Transforms/InstSimplify/cheri-intrinsics-get-set.ll index 43d31dba2b31..a6175798fcde 100644 --- a/llvm/test/Transforms/InstSimplify/cheri-intrinsics-get-set.ll +++ b/llvm/test/Transforms/InstSimplify/cheri-intrinsics-get-set.ll @@ -12,6 +12,8 @@ declare i8 addrspace(200)* @llvm.cheri.cap.INTRINSIC.set.i64(i8 addrspace(200)*, ;; This is a no-op and should be folded to ret %arg +;; NB: The exception here is @llvm.cheri.cap.high.set since it is tag-clearing +;; and therefore could only be used on known-untagged values. define i8 addrspace(200)* @fold_set_of_get(i8 addrspace(200)* %arg) nounwind { ; ADDRESS-LABEL: define {{[^@]+}}@fold_set_of_get ; ADDRESS-SAME: (i8 addrspace(200)* [[ARG:%.*]]) addrspace(200) #[[ATTR0:[0-9]+]] { @@ -85,13 +87,13 @@ define i64 @fold_get_of_set(i8 addrspace(200)* %arg, i64 %value) nounwind { ; ; HIGH-LABEL: define {{[^@]+}}@fold_get_of_set ; HIGH-SAME: (i8 addrspace(200)* [[ARG:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { -; HIGH-NEXT: [[MODIFIED:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.high.set.i64(i8 addrspace(200)* [[ARG]], i64 [[VALUE]]) -; HIGH-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.high.get.i64(i8 addrspace(200)* [[MODIFIED]]) -; HIGH-NEXT: ret i64 [[RET]] +; HIGH-NEXT: ret i64 [[VALUE]] ; ; OFFSET-LABEL: define {{[^@]+}}@fold_get_of_set ; OFFSET-SAME: (i8 addrspace(200)* [[ARG:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { -; OFFSET-NEXT: ret i64 [[VALUE]] +; OFFSET-NEXT: [[MODIFIED:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.offset.set.i64(i8 addrspace(200)* [[ARG]], i64 [[VALUE]]) +; OFFSET-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(i8 addrspace(200)* [[MODIFIED]]) +; OFFSET-NEXT: ret i64 [[RET]] ; %modified = tail call i8 addrspace(200)* @llvm.cheri.cap.INTRINSIC.set.i64(i8 addrspace(200)* %arg, i64 %value) %ret = tail call i64 @llvm.cheri.cap.INTRINSIC.get.i64(i8 addrspace(200)* %modified) @@ -136,8 +138,7 @@ define i64 @fold_get_on_null() nounwind { ; ; HIGH-LABEL: define {{[^@]+}}@fold_get_on_null ; HIGH-SAME: () addrspace(200) #[[ATTR0]] { -; HIGH-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.high.get.i64(i8 addrspace(200)* null) -; HIGH-NEXT: ret i64 [[RET]] +; HIGH-NEXT: ret i64 0 ; ; OFFSET-LABEL: define {{[^@]+}}@fold_get_on_null ; OFFSET-SAME: () addrspace(200) #[[ATTR0]] { @@ -158,9 +159,7 @@ define i64 @fold_get_on_null_with_gep(i64 %value, i64 %gepoff) nounwind { ; ; HIGH-LABEL: define {{[^@]+}}@fold_get_on_null_with_gep ; HIGH-SAME: (i64 [[VALUE:%.*]], i64 [[GEPOFF:%.*]]) addrspace(200) #[[ATTR0]] { -; HIGH-NEXT: [[TMP:%.*]] = getelementptr i8, i8 addrspace(200)* null, i64 [[GEPOFF]] -; HIGH-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.high.get.i64(i8 addrspace(200)* [[TMP]]) -; HIGH-NEXT: ret i64 [[RET]] +; HIGH-NEXT: ret i64 0 ; ; OFFSET-LABEL: define {{[^@]+}}@fold_get_on_null_with_gep ; OFFSET-SAME: (i64 [[VALUE:%.*]], i64 [[GEPOFF:%.*]]) addrspace(200) #[[ATTR0]] { From d9bf74f244eff2ca51ee5e11e7b8d0e6d00a01ac Mon Sep 17 00:00:00 2001 From: Alex Richardson Date: Fri, 12 May 2023 13:35:55 -0700 Subject: [PATCH 4/4] [InstSimplify] Add basic fold for @llvm.cheri.cap.flags.set If we are setting the same value we just read we can simplify the sequence to just the original argument. --- llvm/lib/Analysis/InstructionSimplify.cpp | 5 +++++ .../test/Transforms/InstSimplify/cheri-intrinsics-get-set.ll | 4 +--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp index 38a7e53f9498..3cc428c12ebb 100644 --- a/llvm/lib/Analysis/InstructionSimplify.cpp +++ b/llvm/lib/Analysis/InstructionSimplify.cpp @@ -6149,6 +6149,11 @@ static Value *simplifyBinaryIntrinsic(Function *F, Value *Op0, Value *Op1, return Op0; } break; + case Intrinsic::cheri_cap_flags_set: + if (match(Op1, + m_Intrinsic(m_Specific(Op0)))) + return Op0; + break; case Intrinsic::cheri_cap_offset_set: case Intrinsic::cheri_cap_address_set: { Value *Base = getBasePtrIgnoringCapabilityAddressManipulation(Op0, Q.DL); diff --git a/llvm/test/Transforms/InstSimplify/cheri-intrinsics-get-set.ll b/llvm/test/Transforms/InstSimplify/cheri-intrinsics-get-set.ll index a6175798fcde..43ac026667eb 100644 --- a/llvm/test/Transforms/InstSimplify/cheri-intrinsics-get-set.ll +++ b/llvm/test/Transforms/InstSimplify/cheri-intrinsics-get-set.ll @@ -21,9 +21,7 @@ define i8 addrspace(200)* @fold_set_of_get(i8 addrspace(200)* %arg) nounwind { ; ; FLAGS-LABEL: define {{[^@]+}}@fold_set_of_get ; FLAGS-SAME: (i8 addrspace(200)* [[ARG:%.*]]) addrspace(200) #[[ATTR0:[0-9]+]] { -; FLAGS-NEXT: [[VALUE:%.*]] = tail call i64 @llvm.cheri.cap.flags.get.i64(i8 addrspace(200)* [[ARG]]) -; FLAGS-NEXT: [[RET:%.*]] = tail call i8 addrspace(200)* @llvm.cheri.cap.flags.set.i64(i8 addrspace(200)* [[ARG]], i64 [[VALUE]]) -; FLAGS-NEXT: ret i8 addrspace(200)* [[RET]] +; FLAGS-NEXT: ret i8 addrspace(200)* [[ARG]] ; ; HIGH-LABEL: define {{[^@]+}}@fold_set_of_get ; HIGH-SAME: (i8 addrspace(200)* [[ARG:%.*]]) addrspace(200) #[[ATTR0:[0-9]+]] {