From 3c626c714c9d1521b57ff07b1211f9d21cbeceef Mon Sep 17 00:00:00 2001 From: Denis Antrushin Date: Fri, 22 May 2020 17:03:02 +0300 Subject: [PATCH] [EarlyCSE] Common gc.relocate calls. gc.relocate intrinsic is special in that its second and third operands are not real values, but indices into relocate's parent statepoint list of GC pointers. To be CSE'd, they need special handling in `isEqual()` and `getHashCode()`. Reviewed By: reames Differential Revision: https://reviews.llvm.org/D80445 --- llvm/lib/Transforms/Scalar/EarlyCSE.cpp | 17 ++++++ llvm/test/Transforms/EarlyCSE/gc_relocate.ll | 86 ++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 llvm/test/Transforms/EarlyCSE/gc_relocate.ll diff --git a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp index d8636d3..356e014 100644 --- a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp +++ b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp @@ -41,6 +41,7 @@ #include "llvm/IR/LLVMContext.h" #include "llvm/IR/PassManager.h" #include "llvm/IR/PatternMatch.h" +#include "llvm/IR/Statepoint.h" #include "llvm/IR/Type.h" #include "llvm/IR/Use.h" #include "llvm/IR/Value.h" @@ -456,6 +457,14 @@ template <> struct DenseMapInfo { unsigned DenseMapInfo::getHashValue(CallValue Val) { Instruction *Inst = Val.Inst; + + // gc.relocate is 'special' call: its second and third operands are + // not real values, but indices into statepoint's argument list. + // Get values they point to. + if (const GCRelocateInst *GCR = dyn_cast(Inst)) + return hash_combine(GCR->getOpcode(), GCR->getOperand(0), + GCR->getBasePtr(), GCR->getDerivedPtr()); + // Hash all of the operands as pointers and mix in the opcode. return hash_combine( Inst->getOpcode(), @@ -466,6 +475,14 @@ bool DenseMapInfo::isEqual(CallValue LHS, CallValue RHS) { Instruction *LHSI = LHS.Inst, *RHSI = RHS.Inst; if (LHS.isSentinel() || RHS.isSentinel()) return LHSI == RHSI; + + // See comment above in `getHashValue()`. + if (const GCRelocateInst *GCR1 = dyn_cast(LHSI)) + if (const GCRelocateInst *GCR2 = dyn_cast(RHSI)) + return GCR1->getOperand(0) == GCR2->getOperand(0) && + GCR1->getBasePtr() == GCR2->getBasePtr() && + GCR1->getDerivedPtr() == GCR2->getDerivedPtr(); + return LHSI->isIdenticalTo(RHSI); } diff --git a/llvm/test/Transforms/EarlyCSE/gc_relocate.ll b/llvm/test/Transforms/EarlyCSE/gc_relocate.ll new file mode 100644 index 0000000..712e6f8 --- /dev/null +++ b/llvm/test/Transforms/EarlyCSE/gc_relocate.ll @@ -0,0 +1,86 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -early-cse -S < %s | FileCheck %s + +declare void @func() +declare i32 @"personality_function"() + +define i1 @test_call(i32 addrspace(1)* %in) gc "statepoint-example" { +; CHECK-LABEL: @test_call( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[SAFEPOINT_TOKEN:%.*]] = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @func, i32 0, i32 0, i32 0, i32 0, i32 addrspace(1)* [[IN:%.*]], i32 addrspace(1)* [[IN]]) +; CHECK-NEXT: [[BASE:%.*]] = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token [[SAFEPOINT_TOKEN]], i32 7, i32 7) +; CHECK-NEXT: [[SAFEPOINT_TOKEN2:%.*]] = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @func, i32 0, i32 0, i32 0, i32 0, i32 addrspace(1)* [[BASE]], i32 addrspace(1)* [[BASE]]) +; CHECK-NEXT: br label [[NEXT:%.*]] +; CHECK: next: +; CHECK-NEXT: [[BASE_RELOC:%.*]] = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token [[SAFEPOINT_TOKEN2]], i32 7, i32 7) +; +entry: + %safepoint_token = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @func, i32 0, i32 0, i32 0, i32 0, i32 addrspace(1)* %in, i32 addrspace(1)* %in) + %base = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 7, i32 7) + %derived = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 7, i32 8) + %safepoint_token2 = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @func, i32 0, i32 0, i32 0, i32 0, i32 addrspace(1)* %base, i32 addrspace(1)* %derived) + br label %next + +next: + %base_reloc = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token2, i32 7, i32 7) + %derived_reloc = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token2, i32 7, i32 8) + %cmp1 = icmp eq i32 addrspace(1)* %base_reloc, null + %cmp2 = icmp eq i32 addrspace(1)* %derived_reloc, null + %cmp = and i1 %cmp1, %cmp2 + ret i1 %cmp +} + +; Negative test: Check that relocates from different statepoints are not CSE'd +define i1 @test_two_calls(i32 addrspace(1)* %in1, i32 addrspace(1)* %in2) gc "statepoint-example" { +; CHECK-LABEL: @test_two_calls( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[SAFEPOINT_TOKEN:%.*]] = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @func, i32 0, i32 0, i32 0, i32 0, i32 addrspace(1)* [[IN1:%.*]], i32 addrspace(1)* [[IN2:%.*]]) +; CHECK-NEXT: [[IN1_RELOC1:%.*]] = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token [[SAFEPOINT_TOKEN]], i32 7, i32 7) +; CHECK-NEXT: [[IN2_RELOC1:%.*]] = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token [[SAFEPOINT_TOKEN]], i32 8, i32 8) +; CHECK-NEXT: [[SAFEPOINT_TOKEN2:%.*]] = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @func, i32 0, i32 0, i32 0, i32 0, i32 addrspace(1)* [[IN1_RELOC1]], i32 addrspace(1)* [[IN2_RELOC1]]) +; CHECK-NEXT: [[IN1_RELOC2:%.*]] = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token [[SAFEPOINT_TOKEN2]], i32 7, i32 7) +; CHECK-NEXT: [[IN2_RELOC2:%.*]] = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token [[SAFEPOINT_TOKEN2]], i32 7, i32 8) +; +entry: + %safepoint_token = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @func, i32 0, i32 0, i32 0, i32 0, i32 addrspace(1)* %in1, i32 addrspace(1)* %in2) + %in1.reloc1 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 7, i32 7) + %in2.reloc1 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 8, i32 8) + %safepoint_token2 = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @func, i32 0, i32 0, i32 0, i32 0, i32 addrspace(1)* %in1.reloc1, i32 addrspace(1)* %in2.reloc1) + %in1.reloc2 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token2, i32 7, i32 7) + %in2.reloc2 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token2, i32 7, i32 8) + %cmp1 = icmp eq i32 addrspace(1)* %in1.reloc2, null + %cmp2 = icmp eq i32 addrspace(1)* %in2.reloc2, null + %cmp = and i1 %cmp1, %cmp2 + ret i1 %cmp +} + +; Negative test: Check that relocates from normal and exceptional pathes are not be CSE'd +define i32 addrspace(1)* @test_invoke(i32 addrspace(1)* %in) gc "statepoint-example" personality i32 ()* @"personality_function" { +; CHECK-LABEL: @test_invoke( +; CHECK-NEXT: [[SAFEPOINT_TOKEN:%.*]] = invoke token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @func, i32 0, i32 0, i32 0, i32 0, i32 addrspace(1)* [[IN:%.*]]) +; CHECK-NEXT: to label [[INVOKE_NORMAL_DEST:%.*]] unwind label [[EXCEPTIONAL_RETURN:%.*]] +; CHECK: invoke_normal_dest: +; CHECK-NEXT: [[OUT:%.*]] = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token [[SAFEPOINT_TOKEN]], i32 7, i32 7) +; CHECK-NEXT: ret i32 addrspace(1)* [[OUT]] +; CHECK: exceptional_return: +; CHECK-NEXT: [[LANDING_PAD:%.*]] = landingpad token +; CHECK-NEXT: cleanup +; CHECK-NEXT: [[OUT1:%.*]] = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token [[LANDING_PAD]], i32 7, i32 7) +; CHECK-NEXT: ret i32 addrspace(1)* [[OUT1]] +; + %safepoint_token = invoke token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @func, i32 0, i32 0, i32 0, i32 0, i32 addrspace(1)* %in) + to label %invoke_normal_dest unwind label %exceptional_return + +invoke_normal_dest: + %out = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 7, i32 7) + ret i32 addrspace(1)* %out + +exceptional_return: + %landing_pad = landingpad token + cleanup + %out1 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %landing_pad, i32 7, i32 7) + ret i32 addrspace(1)* %out1 +} + +declare token @llvm.experimental.gc.statepoint.p0f_isVoidf(i64, i32, void ()*, i32, i32, ...) +declare i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token, i32, i32) -- 2.7.4