; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt --instcombine -lower-constant-intrinsics -S < %s | FileCheck %s target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" declare dso_local i32 @posix_memalign(i8** noundef, i64 noundef, i64 noundef) declare i64 @llvm.objectsize.i64.p0i8(i8*, i1 immarg, i1 immarg, i1 immarg) ; Check posix_memalign call with proper handlig of return value define dso_local i64 @check_posix_memalign(i32 noundef %n) local_unnamed_addr { ; CHECK-LABEL: @check_posix_memalign( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[OBJ:%.*]] = alloca i8*, align 8 ; CHECK-NEXT: [[CALL:%.*]] = call i32 @posix_memalign(i8** noundef nonnull [[OBJ]], i64 noundef 8, i64 noundef 10) ; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i32 [[CALL]], 0 ; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label [[COND_FALSE:%.*]], label [[EXIT:%.*]] ; CHECK: cond.false: ; CHECK-NEXT: br label [[EXIT]] ; CHECK: exit: ; CHECK-NEXT: [[COND:%.*]] = phi i64 [ -2, [[ENTRY:%.*]] ], [ 10, [[COND_FALSE]] ] ; CHECK-NEXT: ret i64 [[COND]] ; entry: %obj = alloca i8* %call = call i32 @posix_memalign(i8** noundef %obj, i64 noundef 8, i64 noundef 10) %tobool = icmp ne i32 %call, 0 br i1 %tobool, label %exit, label %cond.false cond.false: %val = load i8*, i8** %obj %objsize = call i64 @llvm.objectsize.i64.p0i8(i8* %val, i1 false, i1 true, i1 false) br label %exit exit: %cond = phi i64 [ -2, %entry ], [ %objsize, %cond.false ] ret i64 %cond } ; Same test case as above but with idiomatic NULL initialization define dso_local i64 @check_posix_memalign_null() { ; CHECK-LABEL: @check_posix_memalign_null( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[OBJ:%.*]] = alloca i8*, align 8 ; CHECK-NEXT: store i8* null, i8** [[OBJ]], align 8 ; CHECK-NEXT: [[CALL:%.*]] = call i32 @posix_memalign(i8** noundef nonnull [[OBJ]], i64 noundef 8, i64 noundef 10) ; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i32 [[CALL]], 0 ; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label [[COND_FALSE:%.*]], label [[EXIT:%.*]] ; CHECK: cond.false: ; CHECK-NEXT: br label [[EXIT]] ; CHECK: exit: ; CHECK-NEXT: [[COND:%.*]] = phi i64 [ -2, [[ENTRY:%.*]] ], [ 10, [[COND_FALSE]] ] ; CHECK-NEXT: ret i64 [[COND]] ; entry: %obj = alloca i8* store i8* null, i8** %obj %call = call i32 @posix_memalign(i8** noundef %obj, i64 noundef 8, i64 noundef 10) %tobool = icmp ne i32 %call, 0 br i1 %tobool, label %exit, label %cond.false cond.false: %val = load i8*, i8** %obj %objsize = call i64 @llvm.objectsize.i64.p0i8(i8* %val, i1 false, i1 true, i1 false) br label %exit exit: %cond = phi i64 [ -2, %entry ], [ %objsize, %cond.false ] ret i64 %cond } ; Using argument storage instead of local storage for the allocated pointer. define dso_local i64 @check_posix_memalign_arg(i8** noalias noundef %obj) { ; CHECK-LABEL: @check_posix_memalign_arg( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[CALL:%.*]] = call i32 @posix_memalign(i8** noundef [[OBJ:%.*]], i64 noundef 8, i64 noundef 10) ; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i32 [[CALL]], 0 ; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label [[COND_FALSE:%.*]], label [[EXIT:%.*]] ; CHECK: cond.false: ; CHECK-NEXT: br label [[EXIT]] ; CHECK: exit: ; CHECK-NEXT: [[COND:%.*]] = phi i64 [ -2, [[ENTRY:%.*]] ], [ 10, [[COND_FALSE]] ] ; CHECK-NEXT: ret i64 [[COND]] ; entry: %call = call i32 @posix_memalign(i8** noundef %obj, i64 noundef 8, i64 noundef 10) %tobool = icmp ne i32 %call, 0 br i1 %tobool, label %exit, label %cond.false cond.false: %val = load i8*, i8** %obj %objsize = call i64 @llvm.objectsize.i64.p0i8(i8* %val, i1 false, i1 true, i1 false) br label %exit exit: %cond = phi i64 [ -2, %entry ], [ %objsize, %cond.false ] ret i64 %cond } ; posix_memalign can fail, in that case no object_size can be guessed. define dso_local i64 @check_posix_memalign_unchecked() { ; CHECK-LABEL: @check_posix_memalign_unchecked( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[OBJ:%.*]] = alloca i8*, align 8 ; CHECK-NEXT: [[CALL:%.*]] = call i32 @posix_memalign(i8** noundef nonnull [[OBJ]], i64 noundef 8, i64 noundef 10) ; CHECK-NEXT: [[VAL:%.*]] = load i8*, i8** [[OBJ]], align 8 ; CHECK-NEXT: ret i64 -1 ; entry: %obj = alloca i8* %call = call i32 @posix_memalign(i8** noundef %obj, i64 noundef 8, i64 noundef 10) %val = load i8*, i8** %obj %objsize = call i64 @llvm.objectsize.i64.p0i8(i8* %val, i1 false, i1 true, i1 false) ret i64 %objsize } ; Checks that bo upon posix_memalign failure behaves correctly define dso_local i64 @check_posix_memalign_inverted_cond() { ; CHECK-LABEL: @check_posix_memalign_inverted_cond( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[OBJ:%.*]] = alloca i8*, align 8 ; CHECK-NEXT: store i8* null, i8** [[OBJ]], align 8 ; CHECK-NEXT: [[CALL:%.*]] = call i32 @posix_memalign(i8** noundef nonnull [[OBJ]], i64 noundef 8, i64 noundef 10) ; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq i32 [[CALL]], 0 ; CHECK-NEXT: br i1 [[TOBOOL]], label [[EXIT:%.*]], label [[COND_FALSE:%.*]] ; CHECK: cond.false: ; CHECK-NEXT: [[VAL:%.*]] = load i8*, i8** [[OBJ]], align 8 ; CHECK-NEXT: br label [[EXIT]] ; CHECK: exit: ; CHECK-NEXT: [[COND:%.*]] = phi i64 [ -2, [[ENTRY:%.*]] ], [ -1, [[COND_FALSE]] ] ; CHECK-NEXT: ret i64 [[COND]] ; entry: %obj = alloca i8* store i8* null, i8** %obj %call = call i32 @posix_memalign(i8** noundef %obj, i64 noundef 8, i64 noundef 10) %tobool = icmp eq i32 %call, 0 br i1 %tobool, label %exit, label %cond.false cond.false: %val = load i8*, i8** %obj %objsize = call i64 @llvm.objectsize.i64.p0i8(i8* %val, i1 false, i1 true, i1 false) br label %exit exit: %cond = phi i64 [ -2, %entry ], [ %objsize, %cond.false ] ret i64 %cond } ; Check posix_memalign call with a runtime condition check define dso_local i64 @check_posix_memalign_runtime_cond(i32 noundef %n) local_unnamed_addr { ; CHECK-LABEL: @check_posix_memalign_runtime_cond( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[OBJ:%.*]] = alloca i8*, align 8 ; CHECK-NEXT: [[CALL:%.*]] = call i32 @posix_memalign(i8** noundef nonnull [[OBJ]], i64 noundef 8, i64 noundef 10) ; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i32 [[CALL]], [[N:%.*]] ; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label [[COND_FALSE:%.*]], label [[EXIT:%.*]] ; CHECK: cond.false: ; CHECK-NEXT: [[VAL:%.*]] = load i8*, i8** [[OBJ]], align 8 ; CHECK-NEXT: br label [[EXIT]] ; CHECK: exit: ; CHECK-NEXT: [[COND:%.*]] = phi i64 [ -2, [[ENTRY:%.*]] ], [ -1, [[COND_FALSE]] ] ; CHECK-NEXT: ret i64 [[COND]] ; entry: %obj = alloca i8* %call = call i32 @posix_memalign(i8** noundef %obj, i64 noundef 8, i64 noundef 10) %tobool = icmp ne i32 %call, %n br i1 %tobool, label %exit, label %cond.false cond.false: %val = load i8*, i8** %obj %objsize = call i64 @llvm.objectsize.i64.p0i8(i8* %val, i1 false, i1 true, i1 false) br label %exit exit: %cond = phi i64 [ -2, %entry ], [ %objsize, %cond.false ] ret i64 %cond } ; Check posix_memalign call with two different paths leading to the same alloc. define dso_local i64 @check_posix_memalign_diamond() local_unnamed_addr { ; CHECK-LABEL: @check_posix_memalign_diamond( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[OBJ:%.*]] = alloca i8*, align 8 ; CHECK-NEXT: [[CALL:%.*]] = call i32 @posix_memalign(i8** noundef nonnull [[OBJ]], i64 noundef 8, i64 noundef 10) ; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i32 [[CALL]], 0 ; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label [[COND_FALSE:%.*]], label [[COND_TRUE:%.*]] ; CHECK: cond.true: ; CHECK-NEXT: br label [[EXIT:%.*]] ; CHECK: cond.false: ; CHECK-NEXT: br label [[EXIT]] ; CHECK: exit: ; CHECK-NEXT: [[COND:%.*]] = phi i64 [ -2, [[COND_TRUE]] ], [ 10, [[COND_FALSE]] ] ; CHECK-NEXT: ret i64 [[COND]] ; entry: %obj = alloca i8* %call = call i32 @posix_memalign(i8** noundef %obj, i64 noundef 8, i64 noundef 10) %tobool = icmp ne i32 %call, 0 br i1 %tobool, label %cond.true, label %cond.false cond.true: br label %exit cond.false: %val = load i8*, i8** %obj %objsize = call i64 @llvm.objectsize.i64.p0i8(i8* %val, i1 false, i1 true, i1 false) br label %exit exit: %cond = phi i64 [ -2, %cond.true ], [ %objsize, %cond.false ] ret i64 %cond }