forked from OSchip/llvm-project
[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
This commit is contained in:
parent
cbfae97ca8
commit
3c626c714c
|
@ -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<CallValue> {
|
|||
|
||||
unsigned DenseMapInfo<CallValue>::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<GCRelocateInst>(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<CallValue>::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<GCRelocateInst>(LHSI))
|
||||
if (const GCRelocateInst *GCR2 = dyn_cast<GCRelocateInst>(RHSI))
|
||||
return GCR1->getOperand(0) == GCR2->getOperand(0) &&
|
||||
GCR1->getBasePtr() == GCR2->getBasePtr() &&
|
||||
GCR1->getDerivedPtr() == GCR2->getDerivedPtr();
|
||||
|
||||
return LHSI->isIdenticalTo(RHSI);
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
Loading…
Reference in New Issue