diff --git a/clang/test/CodeGen/attr-counted-by.c b/clang/test/CodeGen/attr-counted-by.c
index e85f3db1121af..dfdf06587f0e2 100644
--- a/clang/test/CodeGen/attr-counted-by.c
+++ b/clang/test/CodeGen/attr-counted-by.c
@@ -333,7 +333,7 @@ size_t test3_bdos(struct annotated *p) {
 // SANITIZE-WITH-ATTR-NEXT:    [[COUNT50:%.*]] = sext i32 [[DOTCOUNTED_BY_LOAD44]] to i64
 // SANITIZE-WITH-ATTR-NEXT:    [[TMP10:%.*]] = sub nsw i64 [[COUNT50]], [[IDXPROM42]]
 // SANITIZE-WITH-ATTR-NEXT:    [[TMP11:%.*]] = tail call i64 @llvm.smax.i64(i64 [[TMP10]], i64 0)
-// SANITIZE-WITH-ATTR-NEXT:    [[DOTTR:%.*]] = trunc nuw i64 [[TMP11]] to i32
+// SANITIZE-WITH-ATTR-NEXT:    [[DOTTR:%.*]] = trunc nuw nsw i64 [[TMP11]] to i32
 // SANITIZE-WITH-ATTR-NEXT:    [[CONV54:%.*]] = shl i32 [[DOTTR]], 2
 // SANITIZE-WITH-ATTR-NEXT:    [[CONV55:%.*]] = and i32 [[CONV54]], 252
 // SANITIZE-WITH-ATTR-NEXT:    store i32 [[CONV55]], ptr [[ARRAYIDX65]], align 4, !tbaa [[TBAA4]]
diff --git a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
index 8e74b8645fad9..314a5d15f0f88 100644
--- a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
+++ b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
@@ -1212,6 +1212,34 @@ static bool processAnd(BinaryOperator *BinOp, LazyValueInfo *LVI) {
   return true;
 }
 
+static bool processTrunc(TruncInst *TI, LazyValueInfo *LVI) {
+  if (TI->hasNoSignedWrap() && TI->hasNoUnsignedWrap())
+    return false;
+
+  ConstantRange Range =
+      LVI->getConstantRangeAtUse(TI->getOperandUse(0), /*UndefAllowed=*/false);
+  uint64_t DestWidth = TI->getDestTy()->getScalarSizeInBits();
+  bool Changed = false;
+
+  if (!TI->hasNoUnsignedWrap()) {
+    if (Range.getActiveBits() <= DestWidth) {
+      TI->setHasNoUnsignedWrap(true);
+      ++NumNUW;
+      Changed = true;
+    }
+  }
+
+  if (!TI->hasNoSignedWrap()) {
+    if (Range.getMinSignedBits() <= DestWidth) {
+      TI->setHasNoSignedWrap(true);
+      ++NumNSW;
+      Changed = true;
+    }
+  }
+
+  return Changed;
+}
+
 static bool runImpl(Function &F, LazyValueInfo *LVI, DominatorTree *DT,
                     const SimplifyQuery &SQ) {
   bool FnChanged = false;
@@ -1275,6 +1303,9 @@ static bool runImpl(Function &F, LazyValueInfo *LVI, DominatorTree *DT,
       case Instruction::And:
         BBChanged |= processAnd(cast<BinaryOperator>(&II), LVI);
         break;
+      case Instruction::Trunc:
+        BBChanged |= processTrunc(cast<TruncInst>(&II), LVI);
+        break;
       }
     }
 
diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/icmp.ll b/llvm/test/Transforms/CorrelatedValuePropagation/icmp.ll
index 5fa8f4e1b9f29..7fb1e0718795d 100644
--- a/llvm/test/Transforms/CorrelatedValuePropagation/icmp.ll
+++ b/llvm/test/Transforms/CorrelatedValuePropagation/icmp.ll
@@ -339,7 +339,7 @@ define i1 @test12(i32 %x) {
 ; CHECK-NEXT:    [[ZEXT:%.*]] = zext i32 [[X:%.*]] to i64
 ; CHECK-NEXT:    [[MUL:%.*]] = mul nuw nsw i64 [[ZEXT]], 7
 ; CHECK-NEXT:    [[SHR:%.*]] = lshr i64 [[MUL]], 32
-; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i64 [[SHR]] to i32
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc nuw nsw i64 [[SHR]] to i32
 ; CHECK-NEXT:    ret i1 true
 ;
   %zext = zext i32 %x to i64
diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/range.ll b/llvm/test/Transforms/CorrelatedValuePropagation/range.ll
index 03d71fa9b5277..caf1156d79229 100644
--- a/llvm/test/Transforms/CorrelatedValuePropagation/range.ll
+++ b/llvm/test/Transforms/CorrelatedValuePropagation/range.ll
@@ -937,7 +937,7 @@ entry:
 define i1 @ctlz_with_range_metadata(i16 %x) {
 ; CHECK-LABEL: @ctlz_with_range_metadata(
 ; CHECK-NEXT:    [[CTLZ:%.*]] = call i16 @llvm.ctlz.i16(i16 [[X:%.*]], i1 false), !range [[RNG5:![0-9]+]]
-; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i16 [[CTLZ]] to i8
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc nuw nsw i16 [[CTLZ]] to i8
 ; CHECK-NEXT:    ret i1 true
 ;
   %ctlz = call i16 @llvm.ctlz.i16(i16 %x, i1 false), !range !{i16 0, i16 8}
@@ -949,7 +949,7 @@ define i1 @ctlz_with_range_metadata(i16 %x) {
 define i1 @abs_with_range_metadata(i16 %x) {
 ; CHECK-LABEL: @abs_with_range_metadata(
 ; CHECK-NEXT:    [[ABS:%.*]] = call i16 @llvm.abs.i16(i16 [[X:%.*]], i1 false), !range [[RNG5]]
-; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i16 [[ABS]] to i8
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc nuw nsw i16 [[ABS]] to i8
 ; CHECK-NEXT:    ret i1 true
 ;
   %abs = call i16 @llvm.abs.i16(i16 %x, i1 false), !range !{i16 0, i16 8}
diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/sext.ll b/llvm/test/Transforms/CorrelatedValuePropagation/sext.ll
index 0db520bfc68a2..648ef0864bf17 100644
--- a/llvm/test/Transforms/CorrelatedValuePropagation/sext.ll
+++ b/llvm/test/Transforms/CorrelatedValuePropagation/sext.ll
@@ -20,7 +20,7 @@ define void @test1(i32 %n) {
 ; CHECK:       for.body:
 ; CHECK-NEXT:    [[EXT_WIDE1:%.*]] = zext nneg i32 [[A]] to i64
 ; CHECK-NEXT:    call void @use64(i64 [[EXT_WIDE1]])
-; CHECK-NEXT:    [[EXT]] = trunc i64 [[EXT_WIDE1]] to i32
+; CHECK-NEXT:    [[EXT]] = trunc nuw nsw i64 [[EXT_WIDE1]] to i32
 ; CHECK-NEXT:    br label [[FOR_COND]]
 ; CHECK:       for.end:
 ; CHECK-NEXT:    ret void
@@ -55,7 +55,7 @@ define void @test2(i32 %n) {
 ; CHECK:       for.body:
 ; CHECK-NEXT:    [[EXT_WIDE:%.*]] = sext i32 [[A]] to i64
 ; CHECK-NEXT:    call void @use64(i64 [[EXT_WIDE]])
-; CHECK-NEXT:    [[EXT]] = trunc i64 [[EXT_WIDE]] to i32
+; CHECK-NEXT:    [[EXT]] = trunc nsw i64 [[EXT_WIDE]] to i32
 ; CHECK-NEXT:    br label [[FOR_COND]]
 ; CHECK:       for.end:
 ; CHECK-NEXT:    ret void
@@ -87,7 +87,7 @@ define void @test3(i32 %n) {
 ; CHECK:       bb:
 ; CHECK-NEXT:    [[EXT_WIDE1:%.*]] = zext nneg i32 [[N]] to i64
 ; CHECK-NEXT:    call void @use64(i64 [[EXT_WIDE1]])
-; CHECK-NEXT:    [[EXT:%.*]] = trunc i64 [[EXT_WIDE1]] to i32
+; CHECK-NEXT:    [[EXT:%.*]] = trunc nuw nsw i64 [[EXT_WIDE1]] to i32
 ; CHECK-NEXT:    br label [[EXIT]]
 ; CHECK:       exit:
 ; CHECK-NEXT:    ret void
@@ -115,7 +115,7 @@ define void @test4(i32 %n) {
 ; CHECK:       bb:
 ; CHECK-NEXT:    [[EXT_WIDE:%.*]] = sext i32 [[N]] to i64
 ; CHECK-NEXT:    call void @use64(i64 [[EXT_WIDE]])
-; CHECK-NEXT:    [[EXT:%.*]] = trunc i64 [[EXT_WIDE]] to i32
+; CHECK-NEXT:    [[EXT:%.*]] = trunc nsw i64 [[EXT_WIDE]] to i32
 ; CHECK-NEXT:    br label [[EXIT]]
 ; CHECK:       exit:
 ; CHECK-NEXT:    ret void
diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/trunc.ll b/llvm/test/Transforms/CorrelatedValuePropagation/trunc.ll
new file mode 100644
index 0000000000000..9b6604298840d
--- /dev/null
+++ b/llvm/test/Transforms/CorrelatedValuePropagation/trunc.ll
@@ -0,0 +1,108 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=correlated-propagation -S | FileCheck %s
+
+
+define i1 @infer_nuw(i8 range(i8 0, 2) %A, i8 range(i8 0, 2) %B) {
+; CHECK-LABEL: define i1 @infer_nuw(
+; CHECK-SAME: i8 range(i8 0, 2) [[A:%.*]], i8 range(i8 0, 2) [[B:%.*]]) {
+; CHECK-NEXT:    [[XOR:%.*]] = xor i8 [[B]], [[A]]
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc nuw i8 [[XOR]] to i1
+; CHECK-NEXT:    ret i1 [[TRUNC]]
+;
+  %xor = xor i8 %B, %A
+  %trunc = trunc i8 %xor to i1
+  ret i1 %trunc
+}
+
+
+define i4 @infer_nsw(i8 %A) {
+; CHECK-LABEL: define range(i4 -4, 4) i4 @infer_nsw(
+; CHECK-SAME: i8 [[A:%.*]]) {
+; CHECK-NEXT:    [[ASHR:%.*]] = ashr i8 [[A]], 5
+; CHECK-NEXT:    [[B:%.*]] = trunc nsw i8 [[ASHR]] to i4
+; CHECK-NEXT:    ret i4 [[B]]
+;
+  %ashr = ashr i8 %A, 5
+  %result = trunc i8 %ashr to i4
+  ret i4 %result
+}
+
+
+define i8 @infer_nuw_nsw(i16 range(i16 -5, -3) %A, i16 range(i16 -5, -3) %B) {
+; CHECK-LABEL: define range(i8 0, 8) i8 @infer_nuw_nsw(
+; CHECK-SAME: i16 range(i16 -5, -3) [[A:%.*]], i16 range(i16 -5, -3) [[B:%.*]]) {
+; CHECK-NEXT:    [[XOR:%.*]] = xor i16 [[B]], [[A]]
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc nuw nsw i16 [[XOR]] to i8
+; CHECK-NEXT:    ret i8 [[TRUNC]]
+;
+  %xor = xor i16 %B, %A
+  %trunc = trunc i16 %xor to i8
+  ret i8 %trunc
+}
+
+
+define i8 @infer_nsw_from_assume(i16 %x) {
+; CHECK-LABEL: define i8 @infer_nsw_from_assume(
+; CHECK-SAME: i16 [[X:%.*]]) {
+; CHECK-NEXT:    [[ADD:%.*]] = add i16 [[X]], 128
+; CHECK-NEXT:    [[OR_COND_I:%.*]] = icmp ult i16 [[ADD]], 256
+; CHECK-NEXT:    tail call void @llvm.assume(i1 [[OR_COND_I]])
+; CHECK-NEXT:    [[CONV1:%.*]] = trunc nsw i16 [[X]] to i8
+; CHECK-NEXT:    ret i8 [[CONV1]]
+;
+  %add = add i16 %x, 128
+  %or.cond.i = icmp ult i16 %add, 256
+  tail call void @llvm.assume(i1 %or.cond.i)
+  %conv1 = trunc i16 %x to i8
+  ret i8 %conv1
+}
+
+
+define i1 @rust_issue_122734(i8 range(i8 0, 3) %A, i8 range(i8 0, 3) %B) {
+; CHECK-LABEL: define i1 @rust_issue_122734(
+; CHECK-SAME: i8 range(i8 0, 3) [[A:%.*]], i8 range(i8 0, 3) [[B:%.*]]) {
+; CHECK-NEXT:  [[START:.*]]:
+; CHECK-NEXT:    [[LHS:%.*]] = icmp eq i8 [[A]], 2
+; CHECK-NEXT:    [[RHS:%.*]] = icmp eq i8 [[B]], 2
+; CHECK-NEXT:    [[OR:%.*]] = or i1 [[LHS]], [[RHS]]
+; CHECK-NEXT:    [[AND:%.*]] = and i1 [[LHS]], [[RHS]]
+; CHECK-NEXT:    br i1 [[OR]], label %[[IFTRUE:.*]], label %[[IFFALSE:.*]]
+; CHECK:       [[IFTRUE]]:
+; CHECK-NEXT:    [[PHI:%.*]] = phi i1 [ [[XOR2:%.*]], %[[IFFALSE]] ], [ [[AND]], %[[START]] ]
+; CHECK-NEXT:    ret i1 [[PHI]]
+; CHECK:       [[IFFALSE]]:
+; CHECK-NEXT:    [[XOR:%.*]] = xor i8 [[A]], [[B]]
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc nuw i8 [[XOR]] to i1
+; CHECK-NEXT:    [[XOR2]] = xor i1 [[TRUNC]], true
+; CHECK-NEXT:    br label %[[IFTRUE]]
+;
+start:
+  %lhs = icmp eq i8 %A, 2
+  %rhs = icmp eq i8 %B, 2
+  %or = or i1 %lhs, %rhs
+  %and = and i1 %lhs, %rhs
+  br i1 %or, label %iftrue, label %iffalse
+
+iftrue:
+  %phi = phi i1 [ %xor2, %iffalse], [ %and, %start ]
+  ret i1 %phi
+
+iffalse:
+  %xor = xor i8 %A, %B
+  %trunc = trunc i8 %xor to i1
+  %xor2 = xor i1 %trunc, true
+  br label %iftrue
+}
+
+
+define i1 @overdefined_range_negative(i8 %A, i8 %B) {
+; CHECK-LABEL: define i1 @overdefined_range_negative(
+; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) {
+; CHECK-NEXT:    [[XOR:%.*]] = xor i8 [[A]], [[B]]
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[XOR]] to i1
+; CHECK-NEXT:    ret i1 [[TRUNC]]
+;
+  %xor = xor i8 %A, %B
+  %trunc = trunc i8 %xor to i1
+  ret i1 %trunc
+}
diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/zext.ll b/llvm/test/Transforms/CorrelatedValuePropagation/zext.ll
index 44434c696fe37..8e43244a8eb62 100644
--- a/llvm/test/Transforms/CorrelatedValuePropagation/zext.ll
+++ b/llvm/test/Transforms/CorrelatedValuePropagation/zext.ll
@@ -14,7 +14,7 @@ define void @test1(i32 %n) {
 ; CHECK:       for.body:
 ; CHECK-NEXT:    [[EXT_WIDE:%.*]] = zext nneg i32 [[A]] to i64
 ; CHECK-NEXT:    call void @use64(i64 [[EXT_WIDE]])
-; CHECK-NEXT:    [[EXT]] = trunc i64 [[EXT_WIDE]] to i32
+; CHECK-NEXT:    [[EXT]] = trunc nuw nsw i64 [[EXT_WIDE]] to i32
 ; CHECK-NEXT:    br label [[FOR_COND]]
 ; CHECK:       for.end:
 ; CHECK-NEXT:    ret void
@@ -49,7 +49,7 @@ define void @test2(i32 %n) {
 ; CHECK:       for.body:
 ; CHECK-NEXT:    [[EXT_WIDE:%.*]] = zext i32 [[A]] to i64
 ; CHECK-NEXT:    call void @use64(i64 [[EXT_WIDE]])
-; CHECK-NEXT:    [[EXT]] = trunc i64 [[EXT_WIDE]] to i32
+; CHECK-NEXT:    [[EXT]] = trunc nuw i64 [[EXT_WIDE]] to i32
 ; CHECK-NEXT:    br label [[FOR_COND]]
 ; CHECK:       for.end:
 ; CHECK-NEXT:    ret void
@@ -81,7 +81,7 @@ define void @test3(i32 %n) {
 ; CHECK:       bb:
 ; CHECK-NEXT:    [[EXT_WIDE:%.*]] = zext nneg i32 [[N]] to i64
 ; CHECK-NEXT:    call void @use64(i64 [[EXT_WIDE]])
-; CHECK-NEXT:    [[EXT:%.*]] = trunc i64 [[EXT_WIDE]] to i32
+; CHECK-NEXT:    [[EXT:%.*]] = trunc nuw nsw i64 [[EXT_WIDE]] to i32
 ; CHECK-NEXT:    br label [[EXIT]]
 ; CHECK:       exit:
 ; CHECK-NEXT:    ret void
@@ -109,7 +109,7 @@ define void @test4(i32 %n) {
 ; CHECK:       bb:
 ; CHECK-NEXT:    [[EXT_WIDE:%.*]] = zext i32 [[N]] to i64
 ; CHECK-NEXT:    call void @use64(i64 [[EXT_WIDE]])
-; CHECK-NEXT:    [[EXT:%.*]] = trunc i64 [[EXT_WIDE]] to i32
+; CHECK-NEXT:    [[EXT:%.*]] = trunc nuw i64 [[EXT_WIDE]] to i32
 ; CHECK-NEXT:    br label [[EXIT]]
 ; CHECK:       exit:
 ; CHECK-NEXT:    ret void