From: Matt Arsenault Date: Sat, 22 Oct 2022 17:05:54 +0000 (-0700) Subject: llvm-reduce: Try to turn calls into something else X-Git-Tag: upstream/17.0.6~28264 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=4638ba7b45f896132f3ee8c665ed390167b5d6b4;p=platform%2Fupstream%2Fllvm.git llvm-reduce: Try to turn calls into something else Try to turn calls that look like operators into known intrinsics. Also try to turn calls that look like a load or a store into a load or store. --- diff --git a/llvm/test/tools/llvm-reduce/reduce-opcodes-call-typed-pointers.ll b/llvm/test/tools/llvm-reduce/reduce-opcodes-call-typed-pointers.ll new file mode 100644 index 0000000..f944b1d --- /dev/null +++ b/llvm/test/tools/llvm-reduce/reduce-opcodes-call-typed-pointers.ll @@ -0,0 +1,87 @@ +; RUN: llvm-reduce --abort-on-invalid-reduction --delta-passes=opcodes --test FileCheck --test-arg --check-prefix=ALL --test-arg %s --test-arg --input-file %s -o %t +; RUN: FileCheck -check-prefixes=RESULT,ALL %s < %t + +target datalayout = "A5" + +; ALL-LABEL: @call_void_no_args( +; RESULT-NEXT: store volatile i32 0, i32 addrspace(5)* null, align 4 +; RESULT-NEXT: ret void +define void @call_void_no_args() { + call void @void_no_args() + ret void +} + +; ALL-LABEL: @call_load_like_i32( +; RESULT-NEXT: %op = load volatile i32, i32 addrspace(1)* %ptr, align 4 +; RESULT-NEXT: ret i32 %op +define i32 @call_load_like_i32(i32 addrspace(1)* %ptr) { + %op = call i32 @load_like_i32(i32 addrspace(1)* %ptr) + ret i32 %op +} + +; ALL-LABEL: @call_load_like_ptr_ptr( +; RESULT-NEXT: %op = load volatile i32 addrspace(1)*, i32 addrspace(1)* addrspace(3)* %ptr, align 8 +; RESULT-NEXT: ret i32 addrspace(1)* %op +define i32 addrspace(1)* @call_load_like_ptr_ptr(i32 addrspace(1)* addrspace(3)* %ptr) { + %op = call i32 addrspace(1)* @load_like_ptr_ptr(i32 addrspace(1)* addrspace(3)* %ptr) + ret i32 addrspace(1)* %op +} + +; ALL-LABEL: @call_store_like_i16( +; RESULT-NEXT: store volatile i16 %val, i16 addrspace(1)* %ptr, align 2 +; RESULT-NEXT: ret void +define void @call_store_like_i16(i16 %val, i16 addrspace(1)* %ptr) { + call void @store_like_i16(i16 %val, i16 addrspace(1)* %ptr) + ret void +} + +; ALL-LABEL: @call_load_like_ptr_mismatch( +; RESULT-NEXT: %op = call i32 @load_like_ptr_mismatch(i16 addrspace(1)* %ptr) +; RESULT-NEXT: ret i32 %op +define i32 @call_load_like_ptr_mismatch(i16 addrspace(1)* %ptr) { + %op = call i32 @load_like_ptr_mismatch(i16 addrspace(1)* %ptr) + ret i32 %op +} + +; ALL-LABEL: @call_store_like_ptr_store( +; RESULT-NEXT: call +; RESULT-NEXT: ret void +define void @call_store_like_ptr_store(i32 addrspace(3)* %ptr.val, i32 addrspace(1)* %ptr) { + call void @store_like_ptr_store(i32 addrspace(3)* %ptr.val, i32 addrspace(1)* %ptr) + ret void +} + + +; ALL-LABEL: @call_store_like_ptr_store_swap( +; RESULT-NEXT: call +; RESULT-NEXT: ret void +define void @call_store_like_ptr_store_swap(i32 addrspace(1)* %ptr, i32 addrspace(3)* %ptr.val) { + call void @store_like_ptr_store_swap(i32 addrspace(1)* %ptr, i32 addrspace(3)* %ptr.val) + ret void +} + +; ALL-LABEL: @call_store_like_ptr_store_different_element_type( +; RESULT-NEXT: call +; RESULT-NEXT: ret void +define void @call_store_like_ptr_store_different_element_type(i32 addrspace(3)* %ptr.val, i16 addrspace(1)* %ptr) { + call void @store_like_ptr_store_different_element_type(i32 addrspace(3)* %ptr.val, i16 addrspace(1)* %ptr) + ret void +} + +; ALL-LABEL: @call_store_like_ptr_store_different_element_type_swap( +; RESULT-NEXT: call +; RESULT-NEXT: ret void +define void @call_store_like_ptr_store_different_element_type_swap(i16 addrspace(1)* %ptr, i32 addrspace(3)* %ptr.val) { + call void @store_like_ptr_store_different_element_type_swap(i16 addrspace(1)* %ptr, i32 addrspace(3)* %ptr.val) + ret void +} + +declare void @void_no_args() +declare i32 addrspace(1)* @load_like_ptr_ptr(i32 addrspace(1)* addrspace(3)*) +declare i32 @load_like_i32(i32 addrspace(1)*) +declare void @store_like_i16(i16 %val, i16 addrspace(1)* %ptr) +declare i32 @load_like_ptr_mismatch(i16 addrspace(1)*) +declare void @store_like_ptr_store(i32 addrspace(3)* %ptr.val, i32 addrspace(1)* %ptr) +declare void @store_like_ptr_store_swap(i32 addrspace(1)* %ptr, i32 addrspace(3)* %ptr.val) +declare void @store_like_ptr_store_different_element_type(i32 addrspace(3)* %ptr.val, i16 addrspace(1)* %ptr) +declare void @store_like_ptr_store_different_element_type_swap(i16 addrspace(1)*, i32 addrspace(3)*) diff --git a/llvm/test/tools/llvm-reduce/reduce-opcodes-call.ll b/llvm/test/tools/llvm-reduce/reduce-opcodes-call.ll new file mode 100644 index 0000000..c63ba52 --- /dev/null +++ b/llvm/test/tools/llvm-reduce/reduce-opcodes-call.ll @@ -0,0 +1,335 @@ +; RUN: llvm-reduce --abort-on-invalid-reduction --delta-passes=opcodes --test FileCheck --test-arg --check-prefix=ALL --test-arg %s --test-arg --input-file %s -o %t +; RUN: FileCheck -check-prefixes=RESULT,ALL %s < %t + +target datalayout = "A5" + +declare token @llvm.return.token() +declare void @llvm.uses.token(token) + +; ALL-LABEL: @call_token( +; RESULT-NEXT: %token = call token @llvm.return.token() +; RESULT-NEXT: call void @llvm.uses.token(token %token) +; RESULT-NEXT: ret void +define void @call_token() { + %token = call token @llvm.return.token() + call void @llvm.uses.token(token %token) + ret void +} + +; ALL-LABEL: @call_void_0_size_arg( +; RESULT-NEXT: store volatile {} %arg, ptr addrspace(5) null, align 1 +; RESULT-NEXT: ret void +define void @call_void_0_size_arg({} %arg) { + call void @void_0_size_arg({} %arg) + ret void +} + +; ALL-LABEL: @call_return_0_size( +; RESULT-NEXT: %op = load volatile {}, ptr %ptr, align 1 +; RESULT-NEXT: ret {} %op +define {} @call_return_0_size(ptr %ptr) { + %op = call {} @return_0_size(ptr %ptr) + ret {} %op +} + +; ALL-LABEL: define void @call_void_no_args( +; RESULT-NEXT: store volatile i32 0, ptr addrspace(5) null, align 4 +; RESULT-NEXT: ret void +define void @call_void_no_args() { + call void @void_no_args() + ret void +} + +; ALL-LABEL: @call_store_like_i16( +; RESULT-NEXT: store volatile i16 %val, ptr addrspace(1) %ptr, align 2 +; RESULT-NEXT: ret void +define void @call_store_like_i16(i16 %val, ptr addrspace(1) %ptr) { + call void @store_like_i16(i16 %val, ptr addrspace(1) %ptr) + ret void +} + +; ALL-LABEL: @keep_call_store_like_i16( +; ALL-NEXT: call void @store_like_i16(i16 %val, ptr addrspace(1) %ptr) +; ALL-NEXT: ret void +define void @keep_call_store_like_i16(i16 %val, ptr addrspace(1) %ptr) { + call void @store_like_i16(i16 %val, ptr addrspace(1) %ptr) + ret void +} + +; ALL-LABEL: @call_store_like_i16_swap( +; RESULT-NEXT: store volatile i16 %val, ptr addrspace(1) %ptr +; RESULT-NEXT: ret void +define void @call_store_like_i16_swap(ptr addrspace(1) %ptr, i16 %val) { + call void @store_like_i16_swap(ptr addrspace(1) %ptr, i16 %val) + ret void +} + +; ALL-LABEL: @call_store_like_i16_extra_arg( +; RESULT-NEXT: call void @store_like_i16_extra_arg(i16 %val, ptr addrspace(1) %ptr, i32 %extra) +; RESULT-NEXT: ret void +define void @call_store_like_i16_extra_arg(i16 %val, ptr addrspace(1) %ptr, i32 %extra) { + call void @store_like_i16_extra_arg(i16 %val, ptr addrspace(1) %ptr, i32 %extra) + ret void +} + +; ALL-LABEL: @call_store_like_i16_extra_ptr_arg( +; RESULT-NEXT: call void @store_like_i16_extra_ptr_arg(i16 %val, ptr addrspace(1) %ptr, ptr addrspace(1) %extra) +; RESULT-NEXT: ret void +define void @call_store_like_i16_extra_ptr_arg(i16 %val, ptr addrspace(1) %ptr, ptr addrspace(1) %extra) { + call void @store_like_i16_extra_ptr_arg(i16 %val, ptr addrspace(1) %ptr, ptr addrspace(1) %extra) + ret void +} + +; ALL-LABEL: @call_store_like_ptr_store( +; RESULT-NEXT: store volatile ptr addrspace(1) %ptr, ptr addrspace(3) %ptr.val, align 8 +; RESULT-NEXT: ret void +define void @call_store_like_ptr_store(ptr addrspace(3) %ptr.val, ptr addrspace(1) %ptr) { + call void @store_like_ptr_store(ptr addrspace(3) %ptr.val, ptr addrspace(1) %ptr) + ret void +} + +; ALL-LABEL: @call_store_like_ptr_store_swap( +; RESULT-NEXT: store volatile ptr addrspace(3) %ptr.val, ptr addrspace(1) %ptr, align 8 +; RESULT-NEXT: ret void +define void @call_store_like_ptr_store_swap(ptr addrspace(1) %ptr, ptr addrspace(3) %ptr.val) { + call void @store_like_ptr_store_swap(ptr addrspace(1) %ptr, ptr addrspace(3) %ptr.val) + ret void +} + +; ALL-LABEL: @call_store_like_ptr_store_different_element_type( +; RESULT-NEXT: store volatile ptr addrspace(1) %ptr, ptr addrspace(3) %ptr.val, align 8 +; RESULT-NEXT: ret void +define void @call_store_like_ptr_store_different_element_type(ptr addrspace(3) %ptr.val, ptr addrspace(1) %ptr) { + call void @store_like_ptr_store_different_element_type(ptr addrspace(3) %ptr.val, ptr addrspace(1) %ptr) + ret void +} + +; ALL-LABEL: @call_store_like_ptr_store_different_element_type_swap( +; RESULT-NEXT: store volatile ptr addrspace(3) %ptr.val, ptr addrspace(1) %ptr, align 8 +; RESULT-NEXT: ret void +define void @call_store_like_ptr_store_different_element_type_swap(ptr addrspace(1) %ptr, ptr addrspace(3) %ptr.val) { + call void @store_like_ptr_store_different_element_type_swap(ptr addrspace(1) %ptr, ptr addrspace(3) %ptr.val) + ret void +} + +; ALL-LABEL: @call_load_like_i32( +; RESULT-NEXT: %op = load volatile i32, ptr addrspace(1) %ptr, align 4 +; RESULT-NEXT: ret i32 %op +define i32 @call_load_like_i32(ptr addrspace(1) %ptr) { + %op = call i32 @load_like_i32(ptr addrspace(1) %ptr) + ret i32 %op +} + +; ALL-LABEL: @keep_call_load_like_i32( +; ALL-NEXT: %op = call i32 @load_like_i32(ptr addrspace(1) %ptr) +; ALL-NEXT: ret i32 %op +define i32 @keep_call_load_like_i32(ptr addrspace(1) %ptr) { + %op = call i32 @load_like_i32(ptr addrspace(1) %ptr) + ret i32 %op +} + +; ALL-LABEL: @call_load_like_i32_extra_arg( +; RESULT-NEXT: %op = call i32 @load_like_i32_extra_arg(ptr addrspace(1) %ptr, i32 %extra) +; RESULT-NEXT: ret i32 %op +define i32 @call_load_like_i32_extra_arg(ptr addrspace(1) %ptr, i32 %extra) { + %op = call i32 @load_like_i32_extra_arg(ptr addrspace(1) %ptr, i32 %extra) + ret i32 %op +} + +; ALL-LABEL: @call_load_like_ptr_mismatch( +; RESULT-NEXT: %op = load volatile i32, ptr addrspace(1) %ptr, align 4 +; RESULT-NEXT: ret i32 %op +define i32 @call_load_like_ptr_mismatch(ptr addrspace(1) %ptr) { + %op = call i32 @load_like_ptr_mismatch(ptr addrspace(1) %ptr) + ret i32 %op +} + +; ALL-LABEL: @call_load_like_skip_arg( +; RESULT-NEXT: %op = load volatile i32, ptr addrspace(1) %ptr, align 4 +; RESULT-NEXT: ret i32 %op +define i32 @call_load_like_skip_arg(float, ptr addrspace(1) %ptr) { + %op = call i32 @load_like_skip_arg(float poison, ptr addrspace(1) %ptr) + ret i32 %op +} + +; ALL-LABEL: @call_fp_scalar_noargs( +; RESULT-NEXT: %op = load volatile float, ptr addrspace(5) null, align 4 +; RESULT-NEXT: ret float %op +define float @call_fp_scalar_noargs() { + %op = call nsz float @fp_scalar_noargs() + ret float %op +} + +; ALL-LABEL: @call_fp_vector_noargs( +; RESULT-NEXT: %op = load volatile <2 x half>, ptr addrspace(5) null, align 4 +; RESULT-NEXT: ret <2 x half> %op +define <2 x half> @call_fp_vector_noargs() { + %op = call nsz <2 x half> @fp_vector_noargs() + ret <2 x half> %op +} + +; ALL-LABEL: @call_unary_fp_scalar( +; RESULT-NEXT: %op = fneg nsz float %a +; RESULT-NEXT: ret float %op +define float @call_unary_fp_scalar(float %a) { + %op = call nsz float @unary_fp_scalar(float %a) + ret float %op +} + +; ALL-LABEL: @call_unary_fp_vector( +; RESULT-NEXT: %op = fneg nsz <2 x half> %a +; RESULT-NEXT: ret <2 x half> %op +define <2 x half> @call_unary_fp_vector(<2 x half> %a) { + %op = call nsz <2 x half> @unary_fp_vector(<2 x half> %a) + ret <2 x half> %op +} + +; ALL-LABEL: @ignore_undef_args_unary_fp( +; RESULT-NEXT: %op = fneg nnan float %a +; RESULT-NEXT: ret float %op +define float @ignore_undef_args_unary_fp(float %a) { + %op = call nnan float @func_i32_f32_i32(i32 poison, float %a, i32 poison) + ret float %op +} + +; ALL-LABEL: @call_binary_fp_scalar( +; RESULT: %op = fmul afn float %a, %b +; RESULT-NEXT: ret float %op +define float @call_binary_fp_scalar(float %a, float %b) { + %op = call afn float @binary_fp_scalar(float %a, float %b) + ret float %op +} + +; ALL-LABEL: @call_binary_fp_vector( +; RESULT-NEXT: %op = fmul afn <2 x half> %a, %b +; RESULT-NEXT: ret <2 x half> %op +define <2 x half> @call_binary_fp_vector(<2 x half> %a, <2 x half> %b) { + %op = call afn <2 x half> @binary_fp_vector(<2 x half> %a, <2 x half> %b) + ret <2 x half> %op +} + +; ALL-LABEL: @call_ternary_fp_scalar( +; RESULT-NEXT: %op = call afn float @llvm.fma.f32(float %a, float %b, float %c) +; RESULT-NEXT: ret float %op +define float @call_ternary_fp_scalar(float %a, float %b, float %c) { + %op = call afn float @ternary_fp_scalar(float %a, float %b, float %c) + ret float %op +} + +; ALL-LABEL: @call_ternary_fp_vector( +; RESULT-NEXT: %op = call afn <2 x half> @llvm.fma.v2f16(<2 x half> %a, <2 x half> %b, <2 x half> %c) +; RESULT-NEXT: ret <2 x half> %op +define <2 x half> @call_ternary_fp_vector(<2 x half> %a, <2 x half> %b, <2 x half> %c) { + %op = call afn <2 x half> @ternary_fp_vector(<2 x half> %a, <2 x half> %b, <2 x half> %c) + ret <2 x half> %op +} + +; ALL-LABEL: @call_unary_int_scalar( +; RESULT-NEXT: %op = call i32 @llvm.bswap.i32(i32 %a) +; RESULT-NEXT: ret i32 %op +define i32 @call_unary_int_scalar(i32 %a) { + %op = call i32 @unary_int_scalar(i32 %a) + ret i32 %op +} + +; ALL-LABEL: @call_unary_int_vector( +; RESULT-NEXT: %op = call <2 x i16> @llvm.bswap.v2i16(<2 x i16> %a) +; RESULT-NEXT: ret <2 x i16> %op +define <2 x i16> @call_unary_int_vector(<2 x i16> %a) { + %op = call <2 x i16> @unary_int_vector(<2 x i16> %a) + ret <2 x i16> %op +} + +; ALL-LABEL: @call_binary_int_scalar( +; RESULT-NEXT: %op = and i32 %a, %b +; RESULT-NEXT: ret i32 %op +define i32 @call_binary_int_scalar(i32 %a, i32 %b) { + %op = call i32 @binary_int_scalar(i32 %a, i32 %b) + ret i32 %op +} + +; ALL-LABEL: @call_binary_int_vector( +; RESULT-NEXT: %op = and <2 x i16> %a, %b +; RESULT-NEXT: ret <2 x i16> %op +define <2 x i16> @call_binary_int_vector(<2 x i16> %a, <2 x i16> %b) { + %op = call <2 x i16> @binary_int_vector(<2 x i16> %a, <2 x i16> %b) + ret <2 x i16> %op +} + +; ALL-LABEL: @call_ternary_int_scalar( +; RESULT-NEXT: %op = call i32 @llvm.fshl.i32(i32 %a, i32 %b, i32 %c) +; RESULT-NEXT: ret i32 %op +define i32 @call_ternary_int_scalar(i32 %a, i32 %b, i32 %c) { + %op = call i32 @ternary_int_scalar(i32 %a, i32 %b, i32 %c) + ret i32 %op +} + +; ALL-LABEL: @call_ternary_int_vector( +; RESULT-NEXT: %op = call <2 x i16> @llvm.fshl.v2i16(<2 x i16> %a, <2 x i16> %b, <2 x i16> %c) +; RESULT-NEXT: ret <2 x i16> %op +define <2 x i16> @call_ternary_int_vector(<2 x i16> %a, <2 x i16> %b, <2 x i16> %c) { + %op = call <2 x i16> @ternary_int_vector(<2 x i16> %a, <2 x i16> %b, <2 x i16> %c) + ret <2 x i16> %op +} + +; ALL-LABEL: @call_quaternary_int_scalar( +; RESULT-NEXT: %op = call i32 @quaternary_int_scalar(i32 %a, i32 %b, i32 %c, i32 %d) +; RESULT-NEXT: ret i32 %op +define i32 @call_quaternary_int_scalar(i32 %a, i32 %b, i32 %c, i32 %d) { + %op = call i32 @quaternary_int_scalar(i32 %a, i32 %b, i32 %c, i32 %d) + ret i32 %op +} + +; ALL-LABEL: @call_quaternary_int_vector( +; RESULT-NEXT: %op = call <2 x i16> @quaternary_int_vector(<2 x i16> %a, <2 x i16> %b, <2 x i16> %c, <2 x i16> %d) +; RESULT-NEXT: ret <2 x i16> %op +define <2 x i16> @call_quaternary_int_vector(<2 x i16> %a, <2 x i16> %b, <2 x i16> %c, <2 x i16> %d) { + %op = call <2 x i16> @quaternary_int_vector(<2 x i16> %a, <2 x i16> %b, <2 x i16> %c, <2 x i16> %d) + ret <2 x i16> %op +} + +declare void @void_0_size_arg({}) +declare {} @return_0_size(ptr) +declare void @void_no_args() +declare void @store_like_i16(i16, ptr addrspace(1)) +declare void @store_like_i16_swap(ptr addrspace(1), i16) +declare void @store_like_i16_extra_arg(i16, ptr addrspace(1), i32) +declare void @store_like_i16_extra_ptr_arg(i16, ptr addrspace(1), ptr addrspace(1)) +declare void @store_like_ptr_store(ptr addrspace(3), ptr addrspace(1)) +declare void @store_like_ptr_store_swap(ptr addrspace(1), ptr addrspace(3)) +declare void @store_like_ptr_store_different_element_type(ptr addrspace(3), ptr addrspace(1)) +declare void @store_like_ptr_store_different_element_type_swap(ptr addrspace(1), ptr addrspace(3)) +declare i32 @load_like_i32(ptr addrspace(1)) + +declare i32 @load_like_i32_extra_arg(ptr addrspace(1), i32) + +declare i32 @load_like_ptr_mismatch(ptr addrspace(1)) +declare i32 @load_like_skip_arg(float, ptr addrspace(1)) + +declare float @fp_scalar_noargs() +declare i32 @int_scalar_noargs() + +declare <2 x half> @fp_vector_noargs() +declare <2 x i16> @int_vector_noargs() + +declare float @unary_fp_scalar(float) +declare <2 x half> @unary_fp_vector(<2 x half>) +declare float @func_i32_f32_i32(i32, float, i32) + +declare float @binary_fp_scalar(float, float) +declare <2 x half> @binary_fp_vector(<2 x half>, <2 x half>) + +declare float @ternary_fp_scalar(float, float, float) +declare <2 x half> @ternary_fp_vector(<2 x half>, <2 x half>, <2 x half>) + +declare float @quaternary_fp_scalar(float, float, float, float) +declare <2 x half> @quaternary_fp_vector(<2 x half>, <2 x half>, <2 x half>, <2 x half>) + +declare i32 @unary_int_scalar(i32) +declare <2 x i16> @unary_int_vector(<2 x i16>) +declare i32 @binary_int_scalar(i32, i32) +declare <2 x i16> @binary_int_vector(<2 x i16>, <2 x i16>) +declare i32 @ternary_int_scalar(i32, i32, i32) +declare <2 x i16> @ternary_int_vector(<2 x i16>, <2 x i16>, <2 x i16>) +declare i32 @quaternary_int_scalar(i32, i32, i32, i32) +declare <2 x i16> @quaternary_int_vector(<2 x i16>, <2 x i16>, <2 x i16>, <2 x i16>) diff --git a/llvm/tools/llvm-reduce/deltas/ReduceOpcodes.cpp b/llvm/tools/llvm-reduce/deltas/ReduceOpcodes.cpp index 2e51511..75a00ae 100644 --- a/llvm/tools/llvm-reduce/deltas/ReduceOpcodes.cpp +++ b/llvm/tools/llvm-reduce/deltas/ReduceOpcodes.cpp @@ -19,6 +19,12 @@ #include "llvm/IR/Intrinsics.h" #include "llvm/IR/IntrinsicsAMDGPU.h" +// Assume outgoing undef arguments aren't relevant. +// TODO: Maybe skip any trivial constant arguments. +static bool shouldIgnoreArgument(const Value *V) { + return isa(V); +} + static Value *replaceIntrinsic(Module &M, IntrinsicInst *II, Intrinsic::ID NewIID, ArrayRef Tys = None) { @@ -64,8 +70,142 @@ static Value *reduceIntrinsic(Oracle &O, Module &M, IntrinsicInst *II) { } } +/// Look for calls that look like they could be replaced with a load or store. +static bool callLooksLikeLoadStore(CallBase *CB, Value *&DataArg, + Value *&PtrArg) { + const bool IsStore = CB->getType()->isVoidTy(); + + PtrArg = nullptr; + DataArg = nullptr; + for (Value *Arg : CB->args()) { + if (shouldIgnoreArgument(Arg)) + continue; + + if (!Arg->getType()->isSized()) + return false; + + PointerType *PT = dyn_cast(Arg->getType()); + if (!PtrArg && PT) { + // FIXME: Could create bitcast for typed pointers, but roll back unused + // replacement only erases one instruction. + if (!IsStore && !PT->isOpaqueOrPointeeTypeMatches(CB->getType())) + return false; + + PtrArg = Arg; + continue; + } + + if (!IsStore || DataArg) + return false; + + DataArg = Arg; + } + + if (IsStore && !DataArg) { + // FIXME: For typed pointers, use element type? + DataArg = ConstantInt::get(IntegerType::getInt32Ty(CB->getContext()), 0); + } + + // If we didn't find any arguments, we can fill in the pointer. + if (!PtrArg) { + unsigned AS = CB->getModule()->getDataLayout().getAllocaAddrSpace(); + + PointerType *PtrTy = + PointerType::get(DataArg ? DataArg->getType() + : IntegerType::getInt32Ty(CB->getContext()), + AS); + + PtrArg = ConstantPointerNull::get(PtrTy); + } + + // Make sure we don't emit an invalid store with typed pointers. + if (IsStore && DataArg->getType()->getPointerTo( + cast(PtrArg->getType())->getAddressSpace()) != + PtrArg->getType()) + return false; + + return true; +} + +// TODO: Replace 2 pointer argument calls with memcpy +static Value *tryReplaceCallWithLoadStore(Oracle &O, Module &M, CallBase *CB) { + Value *PtrArg = nullptr; + Value *DataArg = nullptr; + if (!callLooksLikeLoadStore(CB, DataArg, PtrArg) || O.shouldKeep()) + return nullptr; + + IRBuilder<> B(CB); + if (DataArg) + return B.CreateStore(DataArg, PtrArg, true); + return B.CreateLoad(CB->getType(), PtrArg, true); +} + +static bool callLooksLikeOperator(CallBase *CB, + SmallVectorImpl &OperatorArgs) { + Type *ReturnTy = CB->getType(); + if (!ReturnTy->isFirstClassType()) + return false; + + for (Value *Arg : CB->args()) { + if (shouldIgnoreArgument(Arg)) + continue; + + if (Arg->getType() != ReturnTy) + return false; + + OperatorArgs.push_back(Arg); + } + + return true; +} + +static Value *tryReplaceCallWithOperator(Oracle &O, Module &M, CallBase *CB) { + SmallVector Arguments; + + if (!callLooksLikeOperator(CB, Arguments) || Arguments.size() > 3) + return nullptr; + + if (O.shouldKeep()) + return nullptr; + + IRBuilder<> B(CB); + if (CB->getType()->isFPOrFPVectorTy()) { + switch (Arguments.size()) { + case 1: + return B.CreateFNeg(Arguments[0]); + case 2: + return B.CreateFMul(Arguments[0], Arguments[1]); + case 3: + return B.CreateIntrinsic(Intrinsic::fma, {CB->getType()}, Arguments); + default: + return nullptr; + } + + llvm_unreachable("all argument sizes handled"); + } + + if (CB->getType()->isIntOrIntVectorTy()) { + switch (Arguments.size()) { + case 1: + return B.CreateUnaryIntrinsic(Intrinsic::bswap, Arguments[0]); + case 2: + return B.CreateAnd(Arguments[0], Arguments[1]); + case 3: + return B.CreateIntrinsic(Intrinsic::fshl, {CB->getType()}, Arguments); + default: + return nullptr; + } + + llvm_unreachable("all argument sizes handled"); + } + + return nullptr; +} + static Value *reduceInstruction(Oracle &O, Module &M, Instruction &I) { IRBuilder<> B(&I); + + // TODO: fp binary operator with constant to fneg switch (I.getOpcode()) { case Instruction::FDiv: case Instruction::FRem: @@ -96,6 +236,14 @@ static Value *reduceInstruction(Oracle &O, Module &M, Instruction &I) { if (IntrinsicInst *II = dyn_cast(&I)) return reduceIntrinsic(O, M, II); + CallBase *CB = cast(&I); + + if (Value *NewOp = tryReplaceCallWithOperator(O, M, CB)) + return NewOp; + + if (Value *NewOp = tryReplaceCallWithLoadStore(O, M, CB)) + return NewOp; + return nullptr; } default: