Index: lib/Transforms/Scalar/RewriteStatepointsForGC.cpp =================================================================== --- lib/Transforms/Scalar/RewriteStatepointsForGC.cpp +++ lib/Transforms/Scalar/RewriteStatepointsForGC.cpp @@ -1317,18 +1317,28 @@ assert(Index < LiveVec.size() && "Bug in std::find?"); return Index; }; - + Module *M = StatepointToken->getModule(); + // All gc_relocate are set to i8 addrspace(1)* type. We originally generated // unique declarations for each pointer type, but this proved problematic // because the intrinsic mangling code is incomplete and fragile. Since // we're moving towards a single unified pointer type anyways, we can just // cast everything to an i8* of the right address space. A bitcast is added // later to convert gc_relocate to the actual value's type. - Module *M = StatepointToken->getModule(); - auto AS = cast(LiveVariables[0]->getType())->getAddressSpace(); - Type *Types[] = {Type::getInt8PtrTy(M->getContext(), AS)}; - Value *GCRelocateDecl = - Intrinsic::getDeclaration(M, Intrinsic::experimental_gc_relocate, Types); + auto getGCRelocateDecl = [&] (Type *Ty) { + assert(isHandledGCPointerType(Ty)); + auto AS = Ty->getScalarType()->getPointerAddressSpace(); + Type *NewTy = Type::getInt8PtrTy(M->getContext(), AS); + if (auto *VT = dyn_cast(Ty)) + NewTy = VectorType::get(NewTy, VT->getNumElements()); + return Intrinsic::getDeclaration(M, Intrinsic::experimental_gc_relocate, + {NewTy}); + }; + + // Lazily populated map from input types to the canonicalized form mentioned + // in the comment above. This should probably be cached somewhere more + // broadly. + DenseMap TypeToDeclMap; for (unsigned i = 0; i < LiveVariables.size(); i++) { // Generate the gc.relocate call and save the result @@ -1336,6 +1346,11 @@ Builder.getInt32(LiveStart + FindIndex(LiveVariables, BasePtrs[i])); Value *LiveIdx = Builder.getInt32(LiveStart + i); + Type *Ty = LiveVariables[i]->getType(); + if (!TypeToDeclMap.count(Ty)) + TypeToDeclMap[Ty] = getGCRelocateDecl(Ty); + Value *GCRelocateDecl = TypeToDeclMap[Ty]; + // only specify a debug name if we can give a useful one CallInst *Reloc = Builder.CreateCall( GCRelocateDecl, {StatepointToken, BaseIdx, LiveIdx}, @@ -2478,7 +2493,8 @@ #ifndef NDEBUG // sanity check for (auto *Ptr : Live) - assert(isGCPointerType(Ptr->getType()) && "must be a gc pointer type"); + assert(isHandledGCPointerType(Ptr->getType()) && + "must be a gc pointer type"); #endif relocationViaAlloca(F, DT, Live, Records); Index: test/Transforms/RewriteStatepointsForGC/deopt-bundles/live-vector-nosplit.ll =================================================================== --- test/Transforms/RewriteStatepointsForGC/deopt-bundles/live-vector-nosplit.ll +++ test/Transforms/RewriteStatepointsForGC/deopt-bundles/live-vector-nosplit.ll @@ -0,0 +1,112 @@ +; Test that we can correctly handle vectors of pointers in statepoint +; rewriting. +; RUN: opt %s -rewrite-statepoints-for-gc -rs4gc-use-deopt-bundles -rs4gc-split-vector-values=0 -S | FileCheck %s + +; A non-vector relocation for comparison +define i64 addrspace(1)* @test(i64 addrspace(1)* %obj) gc "statepoint-example" { +; CHECK-LABEL: test +; CHECK: gc.statepoint +; CHECK-NEXT: gc.relocate +; CHECK-NEXT: bitcast +; CHECK-NEXT: ret i64 addrspace(1)* +; A base vector from a argument +entry: + call void @do_safepoint() [ "deopt"() ] + ret i64 addrspace(1)* %obj +} + +; A vector argument +define <2 x i64 addrspace(1)*> @test2(<2 x i64 addrspace(1)*> %obj) gc "statepoint-example" { +; CHECK-LABEL: test2 +; CHECK-NEXT: gc.statepoint +; CHECK-NEXT: gc.relocate +; CHECK-NEXT: bitcast +; CHECK-NEXT: ret <2 x i64 addrspace(1)*> + call void @do_safepoint() [ "deopt"() ] + ret <2 x i64 addrspace(1)*> %obj +} + +; A load +define <2 x i64 addrspace(1)*> @test3(<2 x i64 addrspace(1)*>* %ptr) gc "statepoint-example" { +; CHECK-LABEL: test3 +; CHECK: load +; CHECK-NEXT: gc.statepoint +; CHECK-NEXT: gc.relocate +; CHECK-NEXT: bitcast +; CHECK-NEXT: ret <2 x i64 addrspace(1)*> +entry: + %obj = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* %ptr + call void @do_safepoint() [ "deopt"() ] + ret <2 x i64 addrspace(1)*> %obj +} + +declare i32 @fake_personality_function() + +; When a statepoint is an invoke rather than a call +define <2 x i64 addrspace(1)*> @test4(<2 x i64 addrspace(1)*>* %ptr) gc "statepoint-example" personality i32 ()* @fake_personality_function { +; CHECK-LABEL: test4 +; CHECK: load +; CHECK-NEXT: gc.statepoint +entry: + %obj = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* %ptr + invoke void @do_safepoint() [ "deopt"() ] + to label %normal_return unwind label %exceptional_return + +normal_return: ; preds = %entry +; CHECK-LABEL: normal_return: +; CHECK: gc.relocate +; CHECK-NEXT: bitcast +; CHECK-NEXT: ret <2 x i64 addrspace(1)*> + ret <2 x i64 addrspace(1)*> %obj + +exceptional_return: ; preds = %entry +; CHECK-LABEL: exceptional_return: +; CHECK: gc.relocate +; CHECK-NEXT: bitcast +; CHECK-NEXT: ret <2 x i64 addrspace(1)*> + %landing_pad4 = landingpad token + cleanup + ret <2 x i64 addrspace(1)*> %obj +} + +; A newly created vector +define <2 x i64 addrspace(1)*> @test5(i64 addrspace(1)* %p) gc "statepoint-example" { +; CHECK-LABEL: test5 +; CHECK: insertelement +; CHECK-NEXT: gc.statepoint +; CHECK-NEXT: gc.relocate +; CHECK-NEXT: bitcast +; CHECK-NEXT: ret <2 x i64 addrspace(1)*> %vec.relocated.casted +entry: + %vec = insertelement <2 x i64 addrspace(1)*> undef, i64 addrspace(1)* %p, i32 0 + call void @do_safepoint() [ "deopt"() ] + ret <2 x i64 addrspace(1)*> %vec +} + +; A merge point +define <2 x i64 addrspace(1)*> @test6(i1 %cnd, <2 x i64 addrspace(1)*>* %ptr) gc "statepoint-example" { +; CHECK-LABEL: test6 +entry: + br i1 %cnd, label %taken, label %untaken + +taken: ; preds = %entry + %obja = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* %ptr + br label %merge + +untaken: ; preds = %entry + %objb = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* %ptr + br label %merge + +merge: ; preds = %untaken, %taken +; CHECK-LABEL: merge: +; CHECK-NEXT: = phi +; CHECK-NEXT: gc.statepoint +; CHECK-NEXT: gc.relocate +; CHECK-NEXT: bitcast +; CHECK-NEXT: ret <2 x i64 addrspace(1)*> + %obj = phi <2 x i64 addrspace(1)*> [ %obja, %taken ], [ %objb, %untaken ] + call void @do_safepoint() [ "deopt"() ] + ret <2 x i64 addrspace(1)*> %obj +} + +declare void @do_safepoint() Index: test/Transforms/RewriteStatepointsForGC/deopt-bundles/live-vector.ll =================================================================== --- test/Transforms/RewriteStatepointsForGC/deopt-bundles/live-vector.ll +++ test/Transforms/RewriteStatepointsForGC/deopt-bundles/live-vector.ll @@ -1,6 +1,6 @@ ; Test that we can correctly handle vectors of pointers in statepoint ; rewriting. Currently, we scalarize, but that's an implementation detail. -; RUN: opt %s -rewrite-statepoints-for-gc -rs4gc-use-deopt-bundles -S | FileCheck %s +; RUN: opt %s -rewrite-statepoints-for-gc -rs4gc-use-deopt-bundles -rs4gc-split-vector-values -S | FileCheck %s ; A non-vector relocation for comparison Index: test/Transforms/RewriteStatepointsForGC/live-vector.ll =================================================================== --- test/Transforms/RewriteStatepointsForGC/live-vector.ll +++ test/Transforms/RewriteStatepointsForGC/live-vector.ll @@ -1,6 +1,6 @@ ; Test that we can correctly handle vectors of pointers in statepoint ; rewriting. Currently, we scalarize, but that's an implementation detail. -; RUN: opt %s -rewrite-statepoints-for-gc -S | FileCheck %s +; RUN: opt %s -rewrite-statepoints-for-gc -rs4gc-split-vector-values -S | FileCheck %s ; A non-vector relocation for comparison define i64 addrspace(1)* @test(i64 addrspace(1)* %obj) gc "statepoint-example" {