Index: llvm/trunk/include/llvm/InitializePasses.h =================================================================== --- llvm/trunk/include/llvm/InitializePasses.h +++ llvm/trunk/include/llvm/InitializePasses.h @@ -295,6 +295,7 @@ void initializeRegionPrinterPass(PassRegistry&); void initializeRegionViewerPass(PassRegistry&); void initializeRegisterCoalescerPass(PassRegistry&); +void initializeStripGCRelocatesPass(PassRegistry&); void initializeRenameIndependentSubregsPass(PassRegistry&); void initializeResetMachineFunctionPass(PassRegistry &); void initializeReversePostOrderFunctionAttrsLegacyPassPass(PassRegistry&); Index: llvm/trunk/include/llvm/Transforms/Scalar.h =================================================================== --- llvm/trunk/include/llvm/Transforms/Scalar.h +++ llvm/trunk/include/llvm/Transforms/Scalar.h @@ -472,6 +472,13 @@ //===----------------------------------------------------------------------===// // +// StripGCRelocates - Remove GC relocates that have been inserted by +// RewriteStatepointsForGC. The resulting IR is incorrect, but this is useful +// for manual inspection. +FunctionPass *createStripGCRelocatesPass(); + +//===----------------------------------------------------------------------===// +// // Float2Int - Demote floats to ints where possible. // FunctionPass *createFloat2IntPass(); Index: llvm/trunk/lib/Transforms/Utils/CMakeLists.txt =================================================================== --- llvm/trunk/lib/Transforms/Utils/CMakeLists.txt +++ llvm/trunk/lib/Transforms/Utils/CMakeLists.txt @@ -35,6 +35,7 @@ ModuleUtils.cpp NameAnonGlobals.cpp PromoteMemoryToRegister.cpp + StripGCRelocates.cpp SSAUpdater.cpp SanitizerStats.cpp SimplifyCFG.cpp Index: llvm/trunk/lib/Transforms/Utils/StripGCRelocates.cpp =================================================================== --- llvm/trunk/lib/Transforms/Utils/StripGCRelocates.cpp +++ llvm/trunk/lib/Transforms/Utils/StripGCRelocates.cpp @@ -0,0 +1,80 @@ +//===- StripGCRelocates.cpp - Remove gc.relocates inserted by RewriteStatePoints===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is a little utility pass that removes the gc.relocates inserted by +// RewriteStatepointsForGC. Note that the generated IR is incorrect, +// but this is useful as a single pass in itself, for analysis of IR, without +// the GC.relocates. The statepoint and gc.result instrinsics would still be +// present. +//===----------------------------------------------------------------------===// + +#include "llvm/IR/Function.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Statepoint.h" +#include "llvm/IR/Type.h" +#include "llvm/Pass.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +namespace { +struct StripGCRelocates : public FunctionPass { + static char ID; // Pass identification, replacement for typeid + StripGCRelocates() : FunctionPass(ID) { + initializeStripGCRelocatesPass(*PassRegistry::getPassRegistry()); + } + + void getAnalysisUsage(AnalysisUsage &Info) const override {} + + bool runOnFunction(Function &F) override; + +}; +char StripGCRelocates::ID = 0; +} + +bool StripGCRelocates::runOnFunction(Function &F) { + // Nothing to do for declarations. + if (F.isDeclaration()) + return false; + SmallVector GCRelocates; + // TODO: We currently do not handle gc.relocates that are in landing pads, + // i.e. not bound to a single statepoint token. + for (Instruction &I : instructions(F)) { + if (auto *GCR = dyn_cast(&I)) + if (isStatepoint(GCR->getOperand(0))) + GCRelocates.push_back(GCR); + } + // All gc.relocates are bound to a single statepoint token. The order of + // visiting gc.relocates for deletion does not matter. + for (GCRelocateInst *GCRel : GCRelocates) { + Value *OrigPtr = GCRel->getDerivedPtr(); + Value *ReplaceGCRel = OrigPtr; + + // All gc_relocates are i8 addrspace(1)* typed, we need a bitcast from i8 + // addrspace(1)* to the type of the OrigPtr, if the are not the same. + if (GCRel->getType() != OrigPtr->getType()) + ReplaceGCRel = new BitCastInst(OrigPtr, GCRel->getType(), "cast", GCRel); + + // Replace all uses of gc.relocate and delete the gc.relocate + // There maybe unncessary bitcasts back to the OrigPtr type, an instcombine + // pass would clear this up. + GCRel->replaceAllUsesWith(ReplaceGCRel); + GCRel->eraseFromParent(); + } + return !GCRelocates.empty(); +} + +INITIALIZE_PASS(StripGCRelocates, "strip-gc-relocates", + "Strip gc.relocates inserted through RewriteStatepointsForGC", + true, false) +FunctionPass *llvm::createStripGCRelocatesPass() { + return new StripGCRelocates(); +} Index: llvm/trunk/lib/Transforms/Utils/Utils.cpp =================================================================== --- llvm/trunk/lib/Transforms/Utils/Utils.cpp +++ llvm/trunk/lib/Transforms/Utils/Utils.cpp @@ -36,6 +36,7 @@ initializeMetaRenamerPass(Registry); initializeMemorySSAWrapperPassPass(Registry); initializeMemorySSAPrinterLegacyPassPass(Registry); + initializeStripGCRelocatesPass(Registry); } /// LLVMInitializeTransformUtils - C binding for initializeTransformUtilsPasses. Index: llvm/trunk/test/Transforms/Util/strip-gc-relocates.ll =================================================================== --- llvm/trunk/test/Transforms/Util/strip-gc-relocates.ll +++ llvm/trunk/test/Transforms/Util/strip-gc-relocates.ll @@ -0,0 +1,120 @@ +; RUN: opt -S -strip-gc-relocates -instcombine < %s | FileCheck %s +; test utility/debugging pass which removes gc.relocates, inserted by -rewrite-statepoints-for-gc +declare void @use_obj32(i32 addrspace(1)*) "gc-leaf-function" + +declare void @g() +declare token @llvm.experimental.gc.statepoint.p0f_isVoidf(i64, i32, void ()*, i32, i32, ...) +declare i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token, i32, i32) #0 +declare void @do_safepoint() + +declare i32 addrspace(1)* @new_instance() #1 + + +; Simple case: remove gc.relocate +define i32 addrspace(1)* @test1(i32 addrspace(1)* %arg) gc "statepoint-example" { +entry: +; CHECK-LABEL: test1 +; CHECK: gc.statepoint +; CHECK-NOT: gc.relocate +; CHECK: ret i32 addrspace(1)* %arg + %statepoint_token = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @g, i32 0, i32 0, i32 0, i32 1, i32 100, i32 addrspace(1)* %arg) + %arg.relocated = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %statepoint_token, i32 8, i32 8) ; (%arg, %arg) + %arg.relocated.casted = bitcast i8 addrspace(1)* %arg.relocated to i32 addrspace(1)* + ret i32 addrspace(1)* %arg.relocated.casted +} + +; Remove gc.relocates in presence of nested relocates. +define void @test2(i32 addrspace(1)* %base) gc "statepoint-example" { +entry: +; CHECK-LABEL: test2 +; CHECK: statepoint +; CHECK-NOT: gc.relocate +; CHECK: call void @use_obj32(i32 addrspace(1)* %ptr.gep1) +; CHECK: call void @use_obj32(i32 addrspace(1)* %ptr.gep1) + %ptr.gep = getelementptr i32, i32 addrspace(1)* %base, i32 15 + %ptr.gep1 = getelementptr i32, i32 addrspace(1)* %ptr.gep, i32 15 + %statepoint_token = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @do_safepoint, i32 0, i32 0, i32 0, i32 0, i32 addrspace(1)* %ptr.gep1, i32 addrspace(1)* %base) + %ptr.gep1.relocated = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %statepoint_token, i32 8, i32 7) ; (%base, %ptr.gep1) + %ptr.gep1.relocated.casted = bitcast i8 addrspace(1)* %ptr.gep1.relocated to i32 addrspace(1)* + %base.relocated = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %statepoint_token, i32 8, i32 8) ; (%base, %base) + %base.relocated.casted = bitcast i8 addrspace(1)* %base.relocated to i32 addrspace(1)* + call void @use_obj32(i32 addrspace(1)* %ptr.gep1.relocated.casted) + %statepoint_token1 = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @do_safepoint, i32 0, i32 0, i32 0, i32 0, i32 addrspace(1)* %ptr.gep1.relocated.casted, i32 addrspace(1)* %base.relocated.casted) + %ptr.gep1.relocated2 = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %statepoint_token1, i32 8, i32 7) ; (%base.relocated.casted, %ptr.gep1.relocated.casted) + %ptr.gep1.relocated2.casted = bitcast i8 addrspace(1)* %ptr.gep1.relocated2 to i32 addrspace(1)* + %base.relocated3 = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %statepoint_token1, i32 8, i32 8) ; (%base.relocated.casted, %base.relocated.casted) + %base.relocated3.casted = bitcast i8 addrspace(1)* %base.relocated3 to i32 addrspace(1)* + call void @use_obj32(i32 addrspace(1)* %ptr.gep1.relocated2.casted) + ret void +} + +; landing pad gc.relocates removed by instcombine since it has no uses. +define i32 addrspace(1)* @test3(i32 addrspace(1)* %arg) gc "statepoint-example" personality i32 8 { +; CHECK-LABEL: test3( +; CHECK: gc.statepoint +; CHECK-LABEL: normal_dest: +; CHECK-NOT: gc.relocate +; CHECK: ret i32 addrspace(1)* %arg +; CHECK-LABEL: unwind_dest: +; CHECK-NOT: gc.relocate +entry: + %statepoint_token = invoke token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @g, i32 0, i32 0, i32 0, i32 1, i32 100, i32 addrspace(1)* %arg) + to label %normal_dest unwind label %unwind_dest + +normal_dest: ; preds = %entry + %arg.relocated1 = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %statepoint_token, i32 8, i32 8) ; (%arg, %arg) + %arg.relocated1.casted = bitcast i8 addrspace(1)* %arg.relocated1 to i32 addrspace(1)* + ret i32 addrspace(1)* %arg.relocated1.casted + +unwind_dest: ; preds = %entry + %lpad = landingpad token + cleanup + %arg.relocated = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %lpad, i32 8, i32 8) ; (%arg, %arg) + %arg.relocated.casted = bitcast i8 addrspace(1)* %arg.relocated to i32 addrspace(1)* + resume token undef +} + +; in presence of phi +define void @test4(i1 %cond) gc "statepoint-example" { +; CHECK-LABEL: test4 +entry: + %base1 = call i32 addrspace(1)* @new_instance() + %base2 = call i32 addrspace(1)* @new_instance() + br i1 %cond, label %here, label %there + +here: ; preds = %entry + br label %merge + +there: ; preds = %entry + br label %merge + +merge: ; preds = %there, %here +; CHECK-LABEL: merge: +; CHECK-NOT: gc.relocate +; CHECK: %ptr.gep.remat = getelementptr i32, i32 addrspace(1)* %basephi.base + %basephi.base = phi i32 addrspace(1)* [ %base1, %here ], [ %base2, %there ], !is_base_value !0 + %basephi = phi i32 addrspace(1)* [ %base1, %here ], [ %base2, %there ] + %ptr.gep = getelementptr i32, i32 addrspace(1)* %basephi, i32 15 + %statepoint_token = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @do_safepoint, i32 0, i32 0, i32 0, i32 0, i32 addrspace(1)* %basephi.base) + %basephi.base.relocated = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %statepoint_token, i32 7, i32 7) ; (%basephi.base, %basephi.base) + %basephi.base.relocated.casted = bitcast i8 addrspace(1)* %basephi.base.relocated to i32 addrspace(1)* + %ptr.gep.remat = getelementptr i32, i32 addrspace(1)* %basephi.base.relocated.casted, i32 15 + call void @use_obj32(i32 addrspace(1)* %ptr.gep.remat) + ret void +} + +; The gc.relocate type is different from %arg, but removing the gc.relocate, +; needs a bitcast to be added from i32 addrspace(1)* to i8 addrspace(1)* +define i8 addrspace(1)* @test5(i32 addrspace(1)* %arg) gc "statepoint-example" { +entry: +; CHECK-LABEL: test5 +; CHECK: gc.statepoint +; CHECK-NOT: gc.relocate + %statepoint_token = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @g, i32 0, i32 0, i32 0, i32 1, i32 100, i32 addrspace(1)* %arg) + %arg.relocated = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %statepoint_token, i32 8, i32 8) ; (%arg, %arg) + ret i8 addrspace(1)* %arg.relocated +} + +attributes #0 = { nounwind readonly } +attributes #1 = { nounwind "gc-leaf-function" } +!0 = !{}