diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index a1f759dd1df83..5e719c6c8cbb7 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -525,28 +525,33 @@ static bool dominatesMergePoint( static ConstantInt *getConstantInt(Value *V, const DataLayout &DL) { // Normal constant int. ConstantInt *CI = dyn_cast(V); - if (CI || !isa(V) || !V->getType()->isPointerTy() || - DL.isNonIntegralPointerType(V->getType())) + if (CI || !isa(V) || !V->getType()->isPointerTy()) return CI; + // It is not safe to look through inttoptr or ptrtoint when using unstable + // pointer types. + if (DL.hasUnstableRepresentation(V->getType())) + return nullptr; + // This is some kind of pointer constant. Turn it into a pointer-sized // ConstantInt if possible. - IntegerType *PtrTy = cast(DL.getIntPtrType(V->getType())); + IntegerType *IntPtrTy = cast(DL.getIntPtrType(V->getType())); // Null pointer means 0, see SelectionDAGBuilder::getValue(const Value*). if (isa(V)) - return ConstantInt::get(PtrTy, 0); + return ConstantInt::get(IntPtrTy, 0); - // IntToPtr const int. + // IntToPtr const int, we can look through this if the semantics of + // inttoptr for this address space are a simple (truncating) bitcast. if (ConstantExpr *CE = dyn_cast(V)) if (CE->getOpcode() == Instruction::IntToPtr) if (ConstantInt *CI = dyn_cast(CE->getOperand(0))) { // The constant is very likely to have the right type already. - if (CI->getType() == PtrTy) + if (CI->getType() == IntPtrTy) return CI; else return cast( - ConstantFoldIntegerCast(CI, PtrTy, /*isSigned=*/false, DL)); + ConstantFoldIntegerCast(CI, IntPtrTy, /*isSigned=*/false, DL)); } return nullptr; } @@ -866,10 +871,12 @@ Value *SimplifyCFGOpt::isValueEqualityComparison(Instruction *TI) { } } - // Unwrap any lossless ptrtoint cast. + // Unwrap any lossless ptrtoint cast (except for unstable pointers). if (CV) { if (PtrToIntInst *PTII = dyn_cast(CV)) { Value *Ptr = PTII->getPointerOperand(); + if (DL.hasUnstableRepresentation(Ptr->getType())) + return CV; if (PTII->getType() == DL.getIntPtrType(Ptr->getType())) CV = Ptr; } @@ -1427,6 +1434,8 @@ bool SimplifyCFGOpt::performValueComparisonIntoPredecessorFolding( Builder.SetInsertPoint(PTI); // Convert pointer to int before we switch. if (CV->getType()->isPointerTy()) { + assert(!DL.hasUnstableRepresentation(CV->getType()) && + "Should not end up here with unstable pointers"); CV = Builder.CreatePtrToInt(CV, DL.getIntPtrType(CV->getType()), "magicptr"); } @@ -5246,6 +5255,8 @@ bool SimplifyCFGOpt::simplifyBranchOnICmpChain(BranchInst *BI, Builder.SetInsertPoint(BI); // Convert pointer to int before we switch. if (CompVal->getType()->isPointerTy()) { + assert(!DL.hasUnstableRepresentation(CompVal->getType()) && + "Should not end up here with unstable pointers"); CompVal = Builder.CreatePtrToInt( CompVal, DL.getIntPtrType(CompVal->getType()), "magicptr"); } diff --git a/llvm/test/Transforms/SimplifyCFG/nonintegral.ll b/llvm/test/Transforms/SimplifyCFG/nonintegral.ll index 423ac4d1e69c1..1bdd436f01d02 100644 --- a/llvm/test/Transforms/SimplifyCFG/nonintegral.ll +++ b/llvm/test/Transforms/SimplifyCFG/nonintegral.ll @@ -1,12 +1,143 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 ; RUN: opt -passes=simplifycfg -S < %s | FileCheck %s -target datalayout = "ni:1" +target datalayout = "pu1:64:64-pe2:64:64:64:32" -define void @test_01(ptr addrspace(1) align 8 %ptr) { -; CHECK-LABEL: @test_01( -; CHECK-NOT: ptrtoint -; CHECK-NEXT: icmp eq ptr addrspace(1) %ptr, null -; CHECK-NOT: ptrtoint +;; TODO: it would probably be better to just emit a pointer compare against null. +define void @test_default_null_base(ptr addrspace(0) align 8 %ptr) { +; CHECK-LABEL: define void @test_default_null_base( +; CHECK-SAME: ptr align 8 [[PTR:%.*]]) { +; CHECK-NEXT: [[MAGICPTR:%.*]] = ptrtoint ptr [[PTR]] to i64 +; CHECK-NEXT: [[COND:%.*]] = icmp eq i64 [[MAGICPTR]], 0 +; CHECK-NEXT: br i1 [[COND]], label %[[TRUE2:.*]], label %[[FALSE1:.*]] +; CHECK: [[FALSE1]]: +; CHECK-NEXT: store i64 1, ptr [[PTR]], align 8 +; CHECK-NEXT: store i64 3, ptr [[PTR]], align 8 +; CHECK-NEXT: br label %[[COMMON_RET:.*]] +; CHECK: [[COMMON_RET]]: +; CHECK-NEXT: ret void +; CHECK: [[TRUE2]]: +; CHECK-NEXT: store i64 2, ptr [[PTR]], align 8 +; CHECK-NEXT: br label %[[COMMON_RET]] +; + %cond1 = icmp eq ptr addrspace(0) %ptr, null + %cond2 = icmp eq ptr addrspace(0) %ptr, null + br i1 %cond1, label %true1, label %false1 + +true1: + br i1 %cond2, label %true2, label %false2 + +false1: + store i64 1, ptr addrspace(0) %ptr, align 8 + br label %true1 + +true2: + store i64 2, ptr addrspace(0) %ptr, align 8 + ret void + +false2: + store i64 3, ptr addrspace(0) %ptr, align 8 + ret void +} + +;; We should not introduce ptrtoint instructions with unstable pointers +define void @test_default_inttoptr_base(ptr addrspace(0) align 8 %ptr) { +; CHECK-LABEL: define void @test_default_inttoptr_base( +; CHECK-SAME: ptr align 8 [[PTR:%.*]]) { +; CHECK-NEXT: [[MAGICPTR:%.*]] = ptrtoint ptr [[PTR]] to i64 +; CHECK-NEXT: [[COND:%.*]] = icmp eq i64 [[MAGICPTR]], 4 +; CHECK-NEXT: br i1 [[COND]], label %[[TRUE2:.*]], label %[[FALSE1:.*]] +; CHECK: [[FALSE1]]: +; CHECK-NEXT: store i64 1, ptr [[PTR]], align 8 +; CHECK-NEXT: store i64 3, ptr [[PTR]], align 8 +; CHECK-NEXT: br label %[[COMMON_RET:.*]] +; CHECK: [[COMMON_RET]]: +; CHECK-NEXT: ret void +; CHECK: [[TRUE2]]: +; CHECK-NEXT: store i64 2, ptr [[PTR]], align 8 +; CHECK-NEXT: br label %[[COMMON_RET]] +; + %cond1 = icmp eq ptr addrspace(0) %ptr, inttoptr (i32 4 to ptr addrspace(0)) + %cond2 = icmp eq ptr addrspace(0) %ptr, inttoptr (i32 4 to ptr addrspace(0)) + br i1 %cond1, label %true1, label %false1 + +true1: + br i1 %cond2, label %true2, label %false2 + +false1: + store i64 1, ptr addrspace(0) %ptr, align 8 + br label %true1 + +true2: + store i64 2, ptr addrspace(0) %ptr, align 8 + ret void + +false2: + store i64 3, ptr addrspace(0) %ptr, align 8 + ret void +} + +;; We should not introduce ptrtoint instructions with unstable pointers +define void @test_default_mixed_base(ptr addrspace(0) align 8 %ptr) { +; CHECK-LABEL: define void @test_default_mixed_base( +; CHECK-SAME: ptr align 8 [[PTR:%.*]]) { +; CHECK-NEXT: [[COND2:%.*]] = icmp eq ptr [[PTR]], null +; CHECK-NEXT: [[MAGICPTR:%.*]] = ptrtoint ptr [[PTR]] to i64 +; CHECK-NEXT: [[COND:%.*]] = icmp eq i64 [[MAGICPTR]], 4 +; CHECK-NEXT: br i1 [[COND]], label %[[FALSE2:.*]], label %[[FALSE1:.*]] +; CHECK: [[FALSE1]]: +; CHECK-NEXT: store i64 1, ptr [[PTR]], align 8 +; CHECK-NEXT: br i1 [[COND2]], label %[[TRUE2:.*]], label %[[FALSE2]] +; CHECK: [[COMMON_RET:.*]]: +; CHECK-NEXT: ret void +; CHECK: [[TRUE2]]: +; CHECK-NEXT: store i64 2, ptr [[PTR]], align 8 +; CHECK-NEXT: br label %[[COMMON_RET]] +; CHECK: [[FALSE2]]: +; CHECK-NEXT: store i64 3, ptr [[PTR]], align 8 +; CHECK-NEXT: br label %[[COMMON_RET]] +; + %cond1 = icmp eq ptr addrspace(0) %ptr, inttoptr (i32 4 to ptr addrspace(0)) + %cond2 = icmp eq ptr addrspace(0) %ptr, null + br i1 %cond1, label %true1, label %false1 + +true1: + br i1 %cond2, label %true2, label %false2 + +false1: + store i64 1, ptr addrspace(0) %ptr, align 8 + br label %true1 + +true2: + store i64 2, ptr addrspace(0) %ptr, align 8 + ret void + +false2: + store i64 3, ptr addrspace(0) %ptr, align 8 + ret void +} + +;; We should not introduce ptrtoint instructions with unstable pointers +define void @test_unstable_null_base(ptr addrspace(1) align 8 %ptr) { +; CHECK-LABEL: define void @test_unstable_null_base( +; CHECK-SAME: ptr addrspace(1) align 8 [[PTR:%.*]]) { +; CHECK-NEXT: [[COND1:%.*]] = icmp eq ptr addrspace(1) [[PTR]], null +; CHECK-NEXT: [[COND2:%.*]] = icmp eq ptr addrspace(1) [[PTR]], null +; CHECK-NEXT: br i1 [[COND1]], label %[[TRUE1:.*]], label %[[FALSE1:.*]] +; CHECK: [[TRUE1]]: +; CHECK-NEXT: br i1 [[COND2]], label %[[TRUE2:.*]], label %[[FALSE2:.*]] +; CHECK: [[FALSE1]]: +; CHECK-NEXT: store i64 1, ptr addrspace(1) [[PTR]], align 8 +; CHECK-NEXT: br label %[[TRUE1]] +; CHECK: [[COMMON_RET:.*]]: +; CHECK-NEXT: ret void +; CHECK: [[TRUE2]]: +; CHECK-NEXT: store i64 2, ptr addrspace(1) [[PTR]], align 8 +; CHECK-NEXT: br label %[[COMMON_RET]] +; CHECK: [[FALSE2]]: +; CHECK-NEXT: store i64 3, ptr addrspace(1) [[PTR]], align 8 +; CHECK-NEXT: br label %[[COMMON_RET]] +; %cond1 = icmp eq ptr addrspace(1) %ptr, null %cond2 = icmp eq ptr addrspace(1) %ptr, null br i1 %cond1, label %true1, label %false1 @@ -26,3 +157,200 @@ false2: store i64 3, ptr addrspace(1) %ptr, align 8 ret void } + +;; We should not introduce ptrtoint instructions with unstable pointers +define void @test_unstable_inttoptr_base(ptr addrspace(1) align 8 %ptr) { +; CHECK-LABEL: define void @test_unstable_inttoptr_base( +; CHECK-SAME: ptr addrspace(1) align 8 [[PTR:%.*]]) { +; CHECK-NEXT: [[COND1:%.*]] = icmp eq ptr addrspace(1) [[PTR]], inttoptr (i32 4 to ptr addrspace(1)) +; CHECK-NEXT: [[COND2:%.*]] = icmp eq ptr addrspace(1) [[PTR]], inttoptr (i32 4 to ptr addrspace(1)) +; CHECK-NEXT: br i1 [[COND1]], label %[[TRUE1:.*]], label %[[FALSE1:.*]] +; CHECK: [[TRUE1]]: +; CHECK-NEXT: br i1 [[COND2]], label %[[TRUE2:.*]], label %[[FALSE2:.*]] +; CHECK: [[FALSE1]]: +; CHECK-NEXT: store i64 1, ptr addrspace(1) [[PTR]], align 8 +; CHECK-NEXT: br label %[[TRUE1]] +; CHECK: [[COMMON_RET:.*]]: +; CHECK-NEXT: ret void +; CHECK: [[TRUE2]]: +; CHECK-NEXT: store i64 2, ptr addrspace(1) [[PTR]], align 8 +; CHECK-NEXT: br label %[[COMMON_RET]] +; CHECK: [[FALSE2]]: +; CHECK-NEXT: store i64 3, ptr addrspace(1) [[PTR]], align 8 +; CHECK-NEXT: br label %[[COMMON_RET]] +; + %cond1 = icmp eq ptr addrspace(1) %ptr, inttoptr (i32 4 to ptr addrspace(1)) + %cond2 = icmp eq ptr addrspace(1) %ptr, inttoptr (i32 4 to ptr addrspace(1)) + br i1 %cond1, label %true1, label %false1 + +true1: + br i1 %cond2, label %true2, label %false2 + +false1: + store i64 1, ptr addrspace(1) %ptr, align 8 + br label %true1 + +true2: + store i64 2, ptr addrspace(1) %ptr, align 8 + ret void + +false2: + store i64 3, ptr addrspace(1) %ptr, align 8 + ret void +} + +;; We should not introduce ptrtoint instructions with unstable pointers +define void @test_unstable_mixed_base(ptr addrspace(1) align 8 %ptr) { +; CHECK-LABEL: define void @test_unstable_mixed_base( +; CHECK-SAME: ptr addrspace(1) align 8 [[PTR:%.*]]) { +; CHECK-NEXT: [[COND1:%.*]] = icmp eq ptr addrspace(1) [[PTR]], inttoptr (i32 4 to ptr addrspace(1)) +; CHECK-NEXT: [[COND2:%.*]] = icmp eq ptr addrspace(1) [[PTR]], null +; CHECK-NEXT: br i1 [[COND1]], label %[[TRUE1:.*]], label %[[FALSE1:.*]] +; CHECK: [[TRUE1]]: +; CHECK-NEXT: br i1 [[COND2]], label %[[TRUE2:.*]], label %[[FALSE2:.*]] +; CHECK: [[FALSE1]]: +; CHECK-NEXT: store i64 1, ptr addrspace(1) [[PTR]], align 8 +; CHECK-NEXT: br label %[[TRUE1]] +; CHECK: [[COMMON_RET:.*]]: +; CHECK-NEXT: ret void +; CHECK: [[TRUE2]]: +; CHECK-NEXT: store i64 2, ptr addrspace(1) [[PTR]], align 8 +; CHECK-NEXT: br label %[[COMMON_RET]] +; CHECK: [[FALSE2]]: +; CHECK-NEXT: store i64 3, ptr addrspace(1) [[PTR]], align 8 +; CHECK-NEXT: br label %[[COMMON_RET]] +; + %cond1 = icmp eq ptr addrspace(1) %ptr, inttoptr (i32 4 to ptr addrspace(1)) + %cond2 = icmp eq ptr addrspace(1) %ptr, null + br i1 %cond1, label %true1, label %false1 + +true1: + br i1 %cond2, label %true2, label %false2 + +false1: + store i64 1, ptr addrspace(1) %ptr, align 8 + br label %true1 + +true2: + store i64 2, ptr addrspace(1) %ptr, align 8 + ret void + +false2: + store i64 3, ptr addrspace(1) %ptr, align 8 + ret void +} + +;; This transformation is fine for pointers with external state. +;; TODO: it would probably be better to just emit a pointer compare against null. +define void @test_external_null_base(ptr addrspace(2) align 8 %ptr) { +; CHECK-LABEL: define void @test_external_null_base( +; CHECK-SAME: ptr addrspace(2) align 8 [[PTR:%.*]]) { +; CHECK-NEXT: [[MAGICPTR:%.*]] = ptrtoint ptr addrspace(2) [[PTR]] to i64 +; CHECK-NEXT: [[COND:%.*]] = icmp eq i64 [[MAGICPTR]], 0 +; CHECK-NEXT: br i1 [[COND]], label %[[TRUE2:.*]], label %[[FALSE1:.*]] +; CHECK: [[FALSE1]]: +; CHECK-NEXT: store i64 1, ptr addrspace(2) [[PTR]], align 8 +; CHECK-NEXT: store i64 3, ptr addrspace(2) [[PTR]], align 8 +; CHECK-NEXT: br label %[[COMMON_RET:.*]] +; CHECK: [[COMMON_RET]]: +; CHECK-NEXT: ret void +; CHECK: [[TRUE2]]: +; CHECK-NEXT: store i64 2, ptr addrspace(2) [[PTR]], align 8 +; CHECK-NEXT: br label %[[COMMON_RET]] +; + %cond1 = icmp eq ptr addrspace(2) %ptr, null + %cond2 = icmp eq ptr addrspace(2) %ptr, null + br i1 %cond1, label %true1, label %false1 + +true1: + br i1 %cond2, label %true2, label %false2 + +false1: + store i64 1, ptr addrspace(2) %ptr, align 8 + br label %true1 + +true2: + store i64 2, ptr addrspace(2) %ptr, align 8 + ret void + +false2: + store i64 3, ptr addrspace(2) %ptr, align 8 + ret void +} + +;; This transformation is fine for pointers with external state (even with inttoptr). +define void @test_external_inttoptr_base(ptr addrspace(2) align 8 %ptr) { +; CHECK-LABEL: define void @test_external_inttoptr_base( +; CHECK-SAME: ptr addrspace(2) align 8 [[PTR:%.*]]) { +; CHECK-NEXT: [[MAGICPTR:%.*]] = ptrtoint ptr addrspace(2) [[PTR]] to i64 +; CHECK-NEXT: [[COND:%.*]] = icmp eq i64 [[MAGICPTR]], 4 +; CHECK-NEXT: br i1 [[COND]], label %[[TRUE2:.*]], label %[[FALSE1:.*]] +; CHECK: [[FALSE1]]: +; CHECK-NEXT: store i64 1, ptr addrspace(2) [[PTR]], align 8 +; CHECK-NEXT: store i64 3, ptr addrspace(2) [[PTR]], align 8 +; CHECK-NEXT: br label %[[COMMON_RET:.*]] +; CHECK: [[COMMON_RET]]: +; CHECK-NEXT: ret void +; CHECK: [[TRUE2]]: +; CHECK-NEXT: store i64 2, ptr addrspace(2) [[PTR]], align 8 +; CHECK-NEXT: br label %[[COMMON_RET]] +; + %cond1 = icmp eq ptr addrspace(2) %ptr, inttoptr (i32 4 to ptr addrspace(2)) + %cond2 = icmp eq ptr addrspace(2) %ptr, inttoptr (i32 4 to ptr addrspace(2)) + br i1 %cond1, label %true1, label %false1 + +true1: + br i1 %cond2, label %true2, label %false2 + +false1: + store i64 1, ptr addrspace(2) %ptr, align 8 + br label %true1 + +true2: + store i64 2, ptr addrspace(2) %ptr, align 8 + ret void + +false2: + store i64 3, ptr addrspace(2) %ptr, align 8 + ret void +} + +;; This transformation is fine for pointers with external state (even with inttoptr). +define void @test_external_mixed_base(ptr addrspace(2) align 8 %ptr) { +; CHECK-LABEL: define void @test_external_mixed_base( +; CHECK-SAME: ptr addrspace(2) align 8 [[PTR:%.*]]) { +; CHECK-NEXT: [[COND2:%.*]] = icmp eq ptr addrspace(2) [[PTR]], null +; CHECK-NEXT: [[MAGICPTR:%.*]] = ptrtoint ptr addrspace(2) [[PTR]] to i64 +; CHECK-NEXT: [[COND:%.*]] = icmp eq i64 [[MAGICPTR]], 4 +; CHECK-NEXT: br i1 [[COND]], label %[[FALSE2:.*]], label %[[FALSE1:.*]] +; CHECK: [[FALSE1]]: +; CHECK-NEXT: store i64 1, ptr addrspace(2) [[PTR]], align 8 +; CHECK-NEXT: br i1 [[COND2]], label %[[TRUE2:.*]], label %[[FALSE2]] +; CHECK: [[COMMON_RET:.*]]: +; CHECK-NEXT: ret void +; CHECK: [[TRUE2]]: +; CHECK-NEXT: store i64 2, ptr addrspace(2) [[PTR]], align 8 +; CHECK-NEXT: br label %[[COMMON_RET]] +; CHECK: [[FALSE2]]: +; CHECK-NEXT: store i64 3, ptr addrspace(2) [[PTR]], align 8 +; CHECK-NEXT: br label %[[COMMON_RET]] +; + %cond1 = icmp eq ptr addrspace(2) %ptr, inttoptr (i32 4 to ptr addrspace(2)) + %cond2 = icmp eq ptr addrspace(2) %ptr, null + br i1 %cond1, label %true1, label %false1 + +true1: + br i1 %cond2, label %true2, label %false2 + +false1: + store i64 1, ptr addrspace(2) %ptr, align 8 + br label %true1 + +true2: + store i64 2, ptr addrspace(2) %ptr, align 8 + ret void + +false2: + store i64 3, ptr addrspace(2) %ptr, align 8 + ret void +} diff --git a/llvm/test/Transforms/SimplifyCFG/switch_create-custom-dl.ll b/llvm/test/Transforms/SimplifyCFG/switch_create-custom-dl.ll index ddf64591776dd..8103124e3e5a6 100644 --- a/llvm/test/Transforms/SimplifyCFG/switch_create-custom-dl.ll +++ b/llvm/test/Transforms/SimplifyCFG/switch_create-custom-dl.ll @@ -1,6 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -S -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1 -switch-range-to-icmp < %s | FileCheck %s -target datalayout="p:40:64:64:32" +target datalayout="p:40:64:64:32-pe200:64:64:64:32-pu201:64:64:64:32" declare void @foo1() @@ -33,14 +33,13 @@ F: ; preds = %0 ret void } -; We need to use finer-grained DataLayout properties for non-integral pointers -; FIXME: Should be using a switch here define void @test1_ptr(ptr %V) { ; CHECK-LABEL: @test1_ptr( -; CHECK-NEXT: [[C1:%.*]] = icmp eq ptr [[V:%.*]], inttoptr (i32 4 to ptr) -; CHECK-NEXT: [[C2:%.*]] = icmp eq ptr [[V]], inttoptr (i32 17 to ptr) -; CHECK-NEXT: [[CN:%.*]] = or i1 [[C1]], [[C2]] -; CHECK-NEXT: br i1 [[CN]], label [[T:%.*]], label [[F:%.*]] +; CHECK-NEXT: [[MAGICPTR:%.*]] = ptrtoint ptr [[V:%.*]] to i40 +; CHECK-NEXT: switch i40 [[MAGICPTR]], label [[F:%.*]] [ +; CHECK-NEXT: i40 17, label [[T:%.*]] +; CHECK-NEXT: i40 4, label [[T]] +; CHECK-NEXT: ] ; CHECK: common.ret: ; CHECK-NEXT: ret void ; CHECK: T: @@ -64,10 +63,11 @@ F: ; preds = %0 define void @test1_ptr_as1(ptr addrspace(1) %V) { ; CHECK-LABEL: @test1_ptr_as1( -; CHECK-NEXT: [[C1:%.*]] = icmp eq ptr addrspace(1) [[V:%.*]], inttoptr (i32 4 to ptr addrspace(1)) -; CHECK-NEXT: [[C2:%.*]] = icmp eq ptr addrspace(1) [[V]], inttoptr (i32 17 to ptr addrspace(1)) -; CHECK-NEXT: [[CN:%.*]] = or i1 [[C1]], [[C2]] -; CHECK-NEXT: br i1 [[CN]], label [[T:%.*]], label [[F:%.*]] +; CHECK-NEXT: [[MAGICPTR:%.*]] = ptrtoint ptr addrspace(1) [[V:%.*]] to i40 +; CHECK-NEXT: switch i40 [[MAGICPTR]], label [[F:%.*]] [ +; CHECK-NEXT: i40 17, label [[T:%.*]] +; CHECK-NEXT: i40 4, label [[T]] +; CHECK-NEXT: ] ; CHECK: common.ret: ; CHECK-NEXT: ret void ; CHECK: T: @@ -89,6 +89,63 @@ F: ; preds = %0 ret void } +; We also allow the transformation for pointers with external state +define void @test1_ptr_external_state(ptr addrspace(200) %V) { +; CHECK-LABEL: @test1_ptr_external_state( +; CHECK-NEXT: [[MAGICPTR:%.*]] = ptrtoint ptr addrspace(200) [[V:%.*]] to i64 +; CHECK-NEXT: switch i64 [[MAGICPTR]], label [[F:%.*]] [ +; CHECK-NEXT: i64 17, label [[T:%.*]] +; CHECK-NEXT: i64 4, label [[T]] +; CHECK-NEXT: ] +; CHECK: common.ret: +; CHECK-NEXT: ret void +; CHECK: T: +; CHECK-NEXT: call void @foo1() +; CHECK-NEXT: br label [[COMMON_RET:%.*]] +; CHECK: F: +; CHECK-NEXT: call void @foo2() +; CHECK-NEXT: br label [[COMMON_RET]] +; + %C1 = icmp eq ptr addrspace(200) %V, inttoptr (i32 4 to ptr addrspace(200)) + %C2 = icmp eq ptr addrspace(200) %V, inttoptr (i32 17 to ptr addrspace(200)) + %CN = or i1 %C1, %C2 ; [#uses=1] + br i1 %CN, label %T, label %F +T: ; preds = %0 + call void @foo1( ) + ret void +F: ; preds = %0 + call void @foo2( ) + ret void +} + +; But it is not permitted for unstable pointer representations +define void @test1_ptr_unstable(ptr addrspace(201) %V) { +; CHECK-LABEL: @test1_ptr_unstable( +; CHECK-NEXT: [[C1:%.*]] = icmp eq ptr addrspace(201) [[V:%.*]], inttoptr (i32 4 to ptr addrspace(201)) +; CHECK-NEXT: [[C2:%.*]] = icmp eq ptr addrspace(201) [[V]], inttoptr (i32 17 to ptr addrspace(201)) +; CHECK-NEXT: [[CN:%.*]] = or i1 [[C1]], [[C2]] +; CHECK-NEXT: br i1 [[CN]], label [[T:%.*]], label [[F:%.*]] +; CHECK: common.ret: +; CHECK-NEXT: ret void +; CHECK: T: +; CHECK-NEXT: call void @foo1() +; CHECK-NEXT: br label [[COMMON_RET:%.*]] +; CHECK: F: +; CHECK-NEXT: call void @foo2() +; CHECK-NEXT: br label [[COMMON_RET]] +; + %C1 = icmp eq ptr addrspace(201) %V, inttoptr (i32 4 to ptr addrspace(201)) + %C2 = icmp eq ptr addrspace(201) %V, inttoptr (i32 17 to ptr addrspace(201)) + %CN = or i1 %C1, %C2 ; [#uses=1] + br i1 %CN, label %T, label %F +T: ; preds = %0 + call void @foo1( ) + ret void +F: ; preds = %0 + call void @foo2( ) + ret void +} + define void @test2(i32 %V) { ; CHECK-LABEL: @test2( ; CHECK-NEXT: switch i32 [[V:%.*]], label [[T:%.*]] [