diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index b6060d9a1e3e..4a90d797bf53 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -4227,21 +4227,14 @@ def StrandMalloc : InheritableAttr { } def Injective : InheritableAttr { - // TODO: Associate this with a single argument, not the function. let Spellings = [Clang<"injective">]; - let Subjects = SubjectList<[Function]>; + let Subjects = SubjectList<[ParmVar]>; let Documentation = [Undocumented]; } def HyperView : InheritableAttr { let Spellings = [Clang<"hyper_view">]; - let Subjects = SubjectList<[FunctionLike]>; - let Documentation = [StrandMallocDocs]; -} - -def HyperToken : InheritableAttr { - let Spellings = [Clang<"hyper_token">]; - let Subjects = SubjectList<[FunctionLike]>; + let Subjects = SubjectList<[ParmVar]>; let Documentation = [StrandMallocDocs]; } diff --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def index e29fe8124d85..b39e8413f99a 100644 --- a/clang/include/clang/Basic/Builtins.def +++ b/clang/include/clang/Basic/Builtins.def @@ -1758,6 +1758,8 @@ BUILTIN(__builtin_ms_va_copy, "vc*&c*&", "n") // Arithmetic Fence: to prevent FP reordering and reassociation optimizations LANGBUILTIN(__arithmetic_fence, "v.", "tE", ALL_LANGUAGES) +BUILTIN(__tapir_frame, "v*", "n") + // Tapir. Rewriting of reducer references happens during sema // and needs a builtin to carry the information to codegen. BUILTIN(__hyper_lookup, "v*vC*z.", "nU") diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 7fafc4f2406d..187c5dacc650 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -5536,15 +5536,27 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, Str.getPointer(), Zeros); return RValue::get(Ptr); } + case Builtin::BI__tapir_frame: { + Function *FF = CGM.getIntrinsic(Intrinsic::tapir_frame); + return RValue::get(Builder.CreateCall(FF, {})); + } case Builtin::BI__hyper_lookup: { + Function *TF = CGM.getIntrinsic(Intrinsic::tapir_frame); + llvm::Value *Frame = Builder.CreateCall(TF, {}); llvm::Value *Size = EmitScalarExpr(E->getArg(1)); Function *F = CGM.getIntrinsic(Intrinsic::hyper_lookup, Size->getType()); llvm::Value *Ptr = EmitScalarExpr(E->getArg(0)); llvm::Value *Identity = EmitScalarExpr(E->getArg(2)); llvm::Value *Reduce = EmitScalarExpr(E->getArg(3)); - return RValue::get(Builder.CreateCall( - F, {Ptr, Size, Builder.CreateBitCast(Identity, VoidPtrTy), - Builder.CreateBitCast(Reduce, VoidPtrTy)})); + CallInst *Call = + Builder.CreateCall(F, + {Frame, Ptr, Size, + Builder.CreateBitCast(Identity, VoidPtrTy), + Builder.CreateBitCast(Reduce, VoidPtrTy)}); + // TODO: These should be added automatically based on the function type. + Call->addParamAttr(1, Attribute::Injective); + Call->addParamAttr(1, Attribute::HyperView); + return RValue::get(Call); } } IsSpawnedScope SpawnedScp(this); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 7a962dd65813..4e441081d159 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -9510,9 +9510,6 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, case ParsedAttr::AT_HyperView: handleSimpleAttribute(S, D, AL); break; - case ParsedAttr::AT_HyperToken: - handleSimpleAttribute(S, D, AL); - break; case ParsedAttr::AT_ReducerUnregister: handleSimpleAttribute(S, D, AL); break; diff --git a/clang/test/Cilk/hyper-array-extern-1.cpp b/clang/test/Cilk/hyper-array-extern-1.cpp index a2fe40185797..9fe10c58b297 100644 --- a/clang/test/Cilk/hyper-array-extern-1.cpp +++ b/clang/test/Cilk/hyper-array-extern-1.cpp @@ -10,7 +10,7 @@ int read_array_hyper(unsigned i) { return x[i]; // CHECK: %[[ARRAYIDX:.+]] = getelementptr inbounds - // CHECK: %[[VIEWRAW:.+]] = call ptr @llvm.hyper.lookup.i64(ptr %[[ARRAYIDX]], i64 4, ptr null, ptr null) + // CHECK: %[[VIEWRAW:.+]] = call ptr @llvm.hyper.lookup.i64(ptr %{{[0-9]+}}, ptr hyper_view injective %[[ARRAYIDX]], i64 4, ptr null, ptr null) // CHECK-NOT: call ptr @llvm.hyper.lookup // CHECK: %[[VAL:.+]] = load i32, ptr %[[VIEWRAW]] // CHECK: ret i32 %[[VAL]] diff --git a/clang/test/Cilk/hyper-assign.c b/clang/test/Cilk/hyper-assign.c index 2c7fe46fd6b5..93bf16362acb 100644 --- a/clang/test/Cilk/hyper-assign.c +++ b/clang/test/Cilk/hyper-assign.c @@ -6,18 +6,18 @@ extern long _Hyperobject x, _Hyperobject y; long chain_assign() { - // CHECK: %[[Y1RAW:.+]] = call ptr @llvm.hyper.lookup.i64(ptr @y, i64 8, ptr null, ptr null) + // CHECK: %[[Y1RAW:.+]] = call ptr @llvm.hyper.lookup.i64(ptr %{{[0-9]+}}, ptr hyper_view injective @y, i64 8, ptr null, ptr null) // CHECK: %[[Y1VAL:.+]] = load i64, ptr %[[Y1RAW]] - // CHECK: call ptr @llvm.hyper.lookup.i64(ptr @x, i64 8, ptr null, ptr null) + // CHECK: call ptr @llvm.hyper.lookup.i64(ptr %{{[0-9]+}}, ptr hyper_view injective @x, i64 8, ptr null, ptr null) // CHECK: store i64 %[[Y1VAL]] - // CHECK: call ptr @llvm.hyper.lookup.i64(ptr @y, i64 8, ptr null, ptr null) - // CHECK: call ptr @llvm.hyper.lookup.i64(ptr @x, i64 8, ptr null, ptr null) + // CHECK: call ptr @llvm.hyper.lookup.i64(ptr %{{[0-9]+}}, ptr hyper_view injective @y, i64 8, ptr null, ptr null) + // CHECK: call ptr @llvm.hyper.lookup.i64(ptr %{{[0-9]+}}, ptr hyper_view injective @x, i64 8, ptr null, ptr null) return x = y = x = y; } long simple_assign(long val) { - // CHECK: call ptr @llvm.hyper.lookup.i64(ptr @x, i64 8, ptr null, ptr null) + // CHECK: call ptr @llvm.hyper.lookup.i64(ptr %{{[0-9]+}}, ptr hyper_view injective @x, i64 8, ptr null, ptr null) // CHECK-NOT: call ptr @llvm.hyper.lookup // CHECK: store i64 return x = val; @@ -26,11 +26,11 @@ long simple_assign(long val) long subtract() { // The order is not fixed here. - // CHECK: call ptr @llvm.hyper.lookup.i64(ptr @y, i64 8, ptr null, ptr null) + // CHECK: call ptr @llvm.hyper.lookup.i64(ptr %{{[0-9]+}}, ptr hyper_view injective @y, i64 8, ptr null, ptr null) // CHECK: load i64 // CHECK: add nsw i64 %[[Y:.+]], 1 // CHECK: store i64 - // CHECK: call ptr @llvm.hyper.lookup.i64(ptr @x, i64 8, ptr null, ptr null) + // CHECK: call ptr @llvm.hyper.lookup.i64(ptr %{{[0-9]+}}, ptr hyper_view injective @x, i64 8, ptr null, ptr null) // CHECK: load i64 // CHECK: sub nsw // CHECK: store i64 diff --git a/clang/test/Cilk/hyper-complex.c b/clang/test/Cilk/hyper-complex.c index 6a76ed70480b..0d46c558b648 100644 --- a/clang/test/Cilk/hyper-complex.c +++ b/clang/test/Cilk/hyper-complex.c @@ -7,7 +7,7 @@ extern __complex__ float _Hyperobject c; // CHECK-LABEL: get_real float get_real() { - // CHECK: %[[RAW1:.+]] = call ptr @llvm.hyper.lookup.i64(ptr @c, i64 8, ptr null, ptr null) + // CHECK: %[[RAW1:.+]] = call ptr @llvm.hyper.lookup.i64(ptr %{{[0-9]+}}, ptr hyper_view injective @c, i64 8, ptr null, ptr null) // CHECK: %[[FIELD1:.+]] = getelementptr inbounds { float, float }, ptr %[[RAW1]], i32 0, i32 0 // CHECK: %[[RET1:.+]] = load float, ptr %[[FIELD1]] // CHECK: ret float %[[RET1]] @@ -16,7 +16,7 @@ float get_real() // CHECK-LABEL: get_imag float get_imag() { - // CHECK: %[[RAW2:.+]] = call ptr @llvm.hyper.lookup.i64(ptr @c, i64 8, ptr null, ptr null) + // CHECK: %[[RAW2:.+]] = call ptr @llvm.hyper.lookup.i64(ptr %{{[0-9]+}}, ptr hyper_view injective @c, i64 8, ptr null, ptr null) // CHECK: %[[FIELD2:.+]] = getelementptr inbounds { float, float }, ptr %[[RAW2]], i32 0, i32 1 // CHECK: load float, ptr %[[FIELD2]] // CHECK: ret float @@ -27,7 +27,7 @@ float get_imag() float get_abs() { // Only one call to llvm.hyper.lookup. - // CHECK: @llvm.hyper.lookup.i64(ptr @c, i64 8, ptr null, ptr null) + // CHECK: @llvm.hyper.lookup.i64(ptr %{{[0-9]+}}, ptr hyper_view injective @c, i64 8, ptr null, ptr null) // CHECK-NOT: @llvm.hyper.lookup // CHECK: call float @cabsf // CHECK: ret float diff --git a/clang/test/Cilk/hyper-copy.c b/clang/test/Cilk/hyper-copy.c index 73eac0728bbd..fe77249307d4 100644 --- a/clang/test/Cilk/hyper-copy.c +++ b/clang/test/Cilk/hyper-copy.c @@ -9,9 +9,9 @@ extern struct S b __attribute__((aligned(8))); // CHECK-LABEL: scopy void scopy() { - // CHECK: call ptr @llvm.hyper.lookup.i64(ptr @a, i64 8, ptr null, ptr null) + // CHECK: call ptr @llvm.hyper.lookup.i64(ptr %{{[0-9]+}}, ptr hyper_view injective @a, i64 8, ptr null, ptr null) // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 @b, - // CHECK: call ptr @llvm.hyper.lookup.i64(ptr @a, i64 8, ptr null, ptr null) + // CHECK: call ptr @llvm.hyper.lookup.i64(ptr %{{[0-9]+}}, ptr hyper_view injective @a, i64 8, ptr null, ptr null) // CHECK: call void @llvm.memcpy.p0.p0.i64 // CHECK: ret void b = a; diff --git a/clang/test/Cilk/hyper-template.cpp b/clang/test/Cilk/hyper-template.cpp index e5a5e78430be..5a83c41d3424 100644 --- a/clang/test/Cilk/hyper-template.cpp +++ b/clang/test/Cilk/hyper-template.cpp @@ -5,7 +5,8 @@ template struct S { T member; }; S _Hyperobject S_long; // CHECK-LABEL: @_Z1fv -// CHECK: %0 = call ptr @llvm.hyper.lookup.i64(ptr @S_long, i64 8, ptr null, ptr null) +// CHECK: %0 = call ptr @llvm.tapir.frame() +// CHECK: %1 = call ptr @llvm.hyper.lookup.i64(ptr %0, ptr hyper_view injective @S_long, i64 8, ptr null, ptr null) // CHECK-NOT: call ptr @llvm.hyper.lookup // CHECK: getelementptr // CHECK: %[[RET:.+]] = load i64 diff --git a/clang/test/Cilk/hyper-unary.c b/clang/test/Cilk/hyper-unary.c index 609420753beb..d5aa8eb6ecb1 100644 --- a/clang/test/Cilk/hyper-unary.c +++ b/clang/test/Cilk/hyper-unary.c @@ -11,9 +11,9 @@ void function1() { // CHECK: store i32 1, ptr %[[Y:.+]], int _Hyperobject y = 1; - // CHECK: call ptr @llvm.hyper.lookup.i64(ptr @x, i64 4, ptr null, ptr null) + // CHECK: call ptr @llvm.hyper.lookup.i64(ptr %{{[0-9]+}}, ptr hyper_view injective @x, i64 4, ptr null, ptr null) // CHECK: load i32 - // CHECK: call ptr @llvm.hyper.lookup.i64(ptr %[[Y]], i64 4, ptr null, ptr null) + // CHECK: call ptr @llvm.hyper.lookup.i64(ptr %{{[0-9]+}}, ptr hyper_view injective %[[Y]], i64 4, ptr null, ptr null) // CHECK: load i32 (void)x; (void)y; } @@ -23,9 +23,9 @@ void function2() { // CHECK: store i32 1, ptr %[[Y:.+]], int _Hyperobject y = 1; - // CHECK: call ptr @llvm.hyper.lookup.i64(ptr @x, i64 4, ptr null, ptr null) + // CHECK: call ptr @llvm.hyper.lookup.i64(ptr %{{[0-9]+}}, ptr hyper_view injective @x, i64 4, ptr null, ptr null) // CHECK: load i32 - // CHECK: call ptr @llvm.hyper.lookup.i64(ptr %[[Y]], i64 4, ptr null, ptr null) + // CHECK: call ptr @llvm.hyper.lookup.i64(ptr %{{[0-9]+}}, ptr hyper_view injective %[[Y]], i64 4, ptr null, ptr null) // CHECK: load i32 (void)!x; (void)!y; } @@ -35,18 +35,18 @@ void function3() { // CHECK: store i32 1, ptr %[[Y:.+]], int _Hyperobject y = 1; - // CHECK: call ptr @llvm.hyper.lookup.i64(ptr @x, i64 4, ptr null, ptr null) + // CHECK: call ptr @llvm.hyper.lookup.i64(ptr %{{[0-9]+}}, ptr hyper_view injective @x, i64 4, ptr null, ptr null) // CHECK: load i32 - // CHECK: call ptr @llvm.hyper.lookup.i64(ptr %[[Y]], i64 4, ptr null, ptr null) + // CHECK: call ptr @llvm.hyper.lookup.i64(ptr %{{[0-9]+}}, ptr hyper_view injective %[[Y]], i64 4, ptr null, ptr null) // CHECK: load i32 (void)-x; (void)-y; - // CHECK: call ptr @llvm.hyper.lookup.i64(ptr @x, i64 4, ptr null, ptr null) + // CHECK: call ptr @llvm.hyper.lookup.i64(ptr %{{[0-9]+}}, ptr hyper_view injective @x, i64 4, ptr null, ptr null) // CHECK: load i32 - // CHECK: call ptr @llvm.hyper.lookup.i64(ptr %[[Y]], i64 4, ptr null, ptr null) + // CHECK: call ptr @llvm.hyper.lookup.i64(ptr %{{[0-9]+}}, ptr hyper_view injective %[[Y]], i64 4, ptr null, ptr null) // CHECK: load i32 (void)~x; (void)~y; // CHECK: %[[XP:.+]] = load ptr, ptr @xp - // CHECK: call ptr @llvm.hyper.lookup.i64(ptr %[[XP]], i64 4, ptr null, ptr null) + // CHECK: call ptr @llvm.hyper.lookup.i64(ptr %{{[0-9]+}}, ptr hyper_view injective %[[XP]], i64 4, ptr null, ptr null) // CHECK: load i32 (void)*xp; } diff --git a/clang/test/Cilk/tapir-frame.c b/clang/test/Cilk/tapir-frame.c new file mode 100644 index 000000000000..253f9c8c402d --- /dev/null +++ b/clang/test/Cilk/tapir-frame.c @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 %s -x c -O1 -fopencilk -mllvm -use-opencilk-runtime-bc=false -mllvm -debug-abi-calls=true -verify -S -emit-llvm -o - | FileCheck %s +// expected-no-diagnostics + +// CHECK-LABEL: zero +int zero() +{ + return __tapir_frame() != 0; + // CHECK: ret i32 0 +} + +// CHECK-LABEL: one +int one() +{ + extern void f(int); + _Cilk_spawn f(0); + _Cilk_spawn f(1); + // CHECK: ret i32 1 + return __tapir_frame() != 0; +} + +// CHECK-LABEL: function3 +int function3() +{ + extern void f(int); + extern int g(void *); + _Cilk_spawn f(0); + // CHECK: call i32 @g(ptr noundef nonnull %__cilkrts_sf) + return g(__tapir_frame()); +} diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test index a3660af8f5ef..0e0238492e22 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -75,12 +75,11 @@ // CHECK-NEXT: GNUInline (SubjectMatchRule_function) // CHECK-NEXT: HIPManaged (SubjectMatchRule_variable) // CHECK-NEXT: Hot (SubjectMatchRule_function) -// CHECK-NEXT: HyperToken (SubjectMatchRule_hasType_functionType) -// CHECK-NEXT: HyperView (SubjectMatchRule_hasType_functionType) +// CHECK-NEXT: HyperView (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: IBAction (SubjectMatchRule_objc_method_is_instance) // CHECK-NEXT: IFunc (SubjectMatchRule_function) // CHECK-NEXT: InitPriority (SubjectMatchRule_variable) -// CHECK-NEXT: Injective (SubjectMatchRule_function) +// CHECK-NEXT: Injective (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: InternalLinkage (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record) // CHECK-NEXT: LTOVisibilityPublic (SubjectMatchRule_record) // CHECK-NEXT: Leaf (SubjectMatchRule_function) diff --git a/llvm/include/llvm/Analysis/BasicAliasAnalysis.h b/llvm/include/llvm/Analysis/BasicAliasAnalysis.h index f5137e9dd79c..7c4f3e5c394b 100644 --- a/llvm/include/llvm/Analysis/BasicAliasAnalysis.h +++ b/llvm/include/llvm/Analysis/BasicAliasAnalysis.h @@ -136,15 +136,19 @@ class BasicAAResult : public AAResultBase { AliasResult aliasCheck(const Value *V1, LocationSize V1Size, const Value *V2, LocationSize V2Size, AAQueryInfo &AAQI, const Instruction *CtxI); + AliasResult aliasCheckTapir(const Value *V1, const Value *V2, + LocationSize Size, AAQueryInfo &AAQI, + const Instruction *CtxI); AliasResult aliasCheckRecursive(const Value *V1, LocationSize V1Size, const Value *V2, LocationSize V2Size, AAQueryInfo &AAQI, const Value *O1, const Value *O2); - AliasResult checkInjectiveArguments(const Value *V1, const Value *O1, - const Value *V2, const Value *O2, + const Value *getViewClass(const CallBase *V, AAQueryInfo &AAQI); + AliasResult checkInjectiveArguments(const CallBase *C1, const CallBase *C2, AAQueryInfo &AAQI); + }; /// Analysis pass providing a never-invalidated alias analysis result. diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td index 51da9f0e79df..6991f3fe1f30 100644 --- a/llvm/include/llvm/IR/Attributes.td +++ b/llvm/include/llvm/IR/Attributes.td @@ -110,7 +110,7 @@ def FnRetThunkExtern : EnumAttr<"fn_ret_thunk_extern", [FnAttr]>; def InAlloca : TypeAttr<"inalloca", [ParamAttr]>; /// Distinct arguments to this function yield distinct return values. -def Injective : EnumAttr<"injective", [FnAttr]>; +def Injective : EnumAttr<"injective", [ParamAttr]>; /// Source said inlining was desirable. def InlineHint : EnumAttr<"inlinehint", [FnAttr]>; @@ -222,7 +222,7 @@ def ReadNone : EnumAttr<"readnone", [ParamAttr]>; def ReadOnly : EnumAttr<"readonly", [ParamAttr]>; /// Tapir reducer-related attributes. -def HyperView : EnumAttr<"hyper_view", [FnAttr]>; +def HyperView : EnumAttr<"hyper_view", [ParamAttr]>; def ReducerRegister : EnumAttr<"reducer_register", [FnAttr]>; def ReducerUnregister : EnumAttr<"reducer_unregister", [FnAttr]>; diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td index b09a7e1ff1b5..55b0bd6b3788 100644 --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -49,6 +49,8 @@ def IntrArgMemOnly : IntrinsicProperty; // accessible by the module being compiled. This is a weaker form of IntrNoMem. def IntrInaccessibleMemOnly : IntrinsicProperty; +def IntrReadInaccessibleMemOnly : IntrinsicProperty; + // IntrInaccessibleMemOrArgMemOnly -- This intrinsic only accesses memory that // its pointer-typed arguments point to or memory that is not accessible // by the module being compiled. This is a weaker form of IntrArgMemOnly. @@ -60,18 +62,10 @@ def Commutative : IntrinsicProperty; // Throws - This intrinsic can throw. def Throws : IntrinsicProperty; -// Injective - This intrinsic returns different values given different -// arguments. -def IntrInjective : IntrinsicProperty; // Strand pure - (Tapir) This intrinsic has no visible side effects and // returns the same result given the same argument, but only within a // single strand of execution. def IntrStrandPure : IntrinsicProperty; -// HyperView - (Tapir) This intrinsic lowers to a runtime call to -// find the address of the current view of a hyperobject. It does not -// return the address of anything in the current frame other than its -// argument. -def IntrHyperView : IntrinsicProperty; // ReducerRegister / ReducerUnregister - (Tapir) This intrinsic registers // or unregisters a reducer. These calls have no side effects on visible // memory but can not be moved past other reducer and hyperobject calls @@ -92,6 +86,22 @@ class NoCapture : IntrinsicProperty { int ArgNo = idx.Value; } +// Injective - This intrinsic returns different values given different +// arguments. +class IntrInjective : IntrinsicProperty { + int ArgNo = idx.Value; + int Bytes = 1; // why is this needed? +} + +// HyperView - (Tapir) This intrinsic lowers to a runtime call to +// find the address of the current view of a hyperobject. It does not +// return the address of anything in the current frame other than its +// argument. +class IntrHyperView : IntrinsicProperty { + int ArgNo = idx.Value; + int Bytes = 1; // why is this needed? +} + // NoAlias - The specified argument pointer is not aliasing other "noalias" pointer // arguments of the intrinsic wrt. the intrinsic scope. class NoAlias : IntrinsicProperty { @@ -1693,10 +1703,10 @@ def int_syncregion_start : Intrinsic<[llvm_token_ty], [], [IntrArgMemOnly, IntrWillReturn]>; def int_tapir_runtime_start - : Intrinsic<[llvm_token_ty], [], [IntrArgMemOnly, IntrWillReturn]>; + : Intrinsic<[llvm_token_ty], [], [IntrInaccessibleMemOnly, IntrWillReturn]>; def int_tapir_runtime_end - : Intrinsic<[], [llvm_token_ty], [IntrArgMemOnly, IntrWillReturn]>; + : Intrinsic<[], [llvm_token_ty], [IntrInaccessibleMemOnly, IntrWillReturn]>; // Intrinsics for taskframes. @@ -1743,25 +1753,33 @@ def int_tapir_loop_grainsize // lowering transforms this intrinsic into ordinary frameaddress // intrinsics. def int_task_frameaddress - : Intrinsic<[llvm_ptr_ty], [llvm_i32_ty], [IntrWillReturn]>; + : Intrinsic<[llvm_ptr_ty], [llvm_i32_ty], + [IntrWillReturn,IntrStrandPure]>; + +// Return the stack_frame in an OpenCilk function, otherwise null. +def int_tapir_frame + : Intrinsic<[llvm_ptr_ty], [], [IntrWillReturn,IntrReadInaccessibleMemOnly]>; // Ideally the types would be [llvm_anyptr_ty], [LLVMMatchType<0>] // but that does not work, so rely on the front end to insert bitcasts. def int_hyper_lookup : Intrinsic<[llvm_ptr_ty], - [llvm_ptr_ty, llvm_anyint_ty, llvm_ptr_ty, llvm_ptr_ty], [ - IntrWillReturn, IntrReadMem, IntrInaccessibleMemOnly, - IntrStrandPure, IntrHyperView, IntrInjective - ]>; + [llvm_ptr_ty, llvm_ptr_ty, llvm_anyint_ty, + llvm_ptr_ty, llvm_ptr_ty], [ + NonNull, NonNull>, + IntrWillReturn, IntrReadMem, IntrReadInaccessibleMemOnly, + IntrStrandPure, IntrHyperView>, + IntrInjective>]>; // Dereferenceable, // TODO: Change tablegen to allow function pointer types in intrinsics. def int_reducer_register : Intrinsic<[], [llvm_ptr_ty, llvm_anyint_ty, llvm_ptr_ty, llvm_ptr_ty], - [IntrWillReturn, IntrInaccessibleMemOnly, IntrReducerRegister]>; + [NonNull>, IntrWillReturn, IntrInaccessibleMemOnly, + IntrReducerRegister]>; def int_reducer_unregister - : Intrinsic<[], [llvm_ptr_ty], [IntrWillReturn, IntrInaccessibleMemOnly, - IntrReducerUnregister]>; + : Intrinsic<[], [llvm_ptr_ty], [NonNull>, IntrWillReturn, + IntrInaccessibleMemOnly, IntrReducerUnregister]>; ///===-------------------------- Other Intrinsics --------------------------===// // diff --git a/llvm/include/llvm/Transforms/Tapir/LoweringUtils.h b/llvm/include/llvm/Transforms/Tapir/LoweringUtils.h index 27fa8f2e465c..2f2b3d7d6d70 100644 --- a/llvm/include/llvm/Transforms/Tapir/LoweringUtils.h +++ b/llvm/include/llvm/Transforms/Tapir/LoweringUtils.h @@ -245,6 +245,9 @@ class TapirTarget { /// Lower a Tapir sync instruction SI. virtual void lowerSync(SyncInst &SI) = 0; + virtual void lowerFrameCall(CallBase *Call, DominatorTree &DT) { + } + virtual void lowerReducerOperation(CallBase *Call) { } diff --git a/llvm/include/llvm/Transforms/Tapir/OpenCilkABI.h b/llvm/include/llvm/Transforms/Tapir/OpenCilkABI.h index a4bae8a902ca..c05a8cf8d685 100644 --- a/llvm/include/llvm/Transforms/Tapir/OpenCilkABI.h +++ b/llvm/include/llvm/Transforms/Tapir/OpenCilkABI.h @@ -152,6 +152,8 @@ class OpenCilkABI final : public TapirTarget { BasicBlock *GetDefaultSyncLandingpad(Function &F, Value *SF, DebugLoc Loc); + Value *getValidFrame(CallBase *FrameCall, DominatorTree &DT); + public: OpenCilkABI(Module &M); ~OpenCilkABI() { DetachCtxToStackFrame.clear(); } @@ -159,8 +161,9 @@ class OpenCilkABI final : public TapirTarget { void setOptions(const TapirTargetOptions &Options) override final; void prepareModule() override final; - Value *lowerGrainsizeCall(CallInst *GrainsizeCall) override final; - void lowerSync(SyncInst &SI) override final; + Value *lowerGrainsizeCall(CallInst *GrainsizeCall) override; + void lowerSync(SyncInst &SI) override; + void lowerFrameCall(CallBase *CI, DominatorTree &DT) override; void lowerReducerOperation(CallBase *CI) override; ArgStructMode getArgStructMode() const override final { diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp index 41270c2ad116..82d803a6f425 100644 --- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp +++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp @@ -844,7 +844,13 @@ AliasResult BasicAAResult::alias(const MemoryLocation &LocA, const Instruction *CtxI) { assert(notDifferentParent(LocA.Ptr, LocB.Ptr) && "BasicAliasAnalysis doesn't support interprocedural queries."); - return aliasCheck(LocA.Ptr, LocA.Size, LocB.Ptr, LocB.Size, AAQI, CtxI); + AliasResult Result = + aliasCheck(LocA.Ptr, LocA.Size, LocB.Ptr, LocB.Size, AAQI, CtxI); + if (Result != AliasResult::MayAlias) + return Result; + if (LocA.Size != LocB.Size) + return AliasResult::MayAlias; + return aliasCheckTapir(LocA.Ptr, LocB.Ptr, LocA.Size, AAQI, CtxI); } /// Checks to see if the specified callsite can clobber the specified memory @@ -1430,269 +1436,139 @@ AliasResult BasicAAResult::aliasPHI(const PHINode *PN, LocationSize PNSize, return Alias; } -// Given that O1 != O2, return NoAlias if they can not alias. -static AliasResult UnderlyingNoAlias(const Value *O1, const Value *O2, - AAQueryInfo &AAQI) { - assert(O1 != O2 && "identical arguments to UnderlyingNoAlias"); - - // If V1/V2 point to two different objects, we know that we have no alias. - if (AAQI.AssumeSameSpindle) { - if (isIdentifiedObjectIfInSameSpindle(O1) && - isIdentifiedObjectIfInSameSpindle(O2)) - return AliasResult::NoAlias; - } else { - if (isIdentifiedObject(O1) && isIdentifiedObject(O2)) - return AliasResult::NoAlias; - } - - // Constant pointers can't alias with non-const isIdentifiedObject objects. - if ((isa(O1) && isIdentifiedObject(O2) && !isa(O2)) || - (isa(O2) && isIdentifiedObject(O1) && !isa(O1))) - return AliasResult::NoAlias; - - // Function arguments can't alias with things that are known to be - // unambigously identified at the function level. - if ((isa(O1) && isIdentifiedFunctionLocal(O2)) || - (isa(O2) && isIdentifiedFunctionLocal(O1))) - return AliasResult::NoAlias; - - // If one pointer is the result of a call/invoke or load and the other is a - // non-escaping local object within the same function, then we know the - // object couldn't escape to a point where the call could return it. - // - // Note that if the pointers are in different functions, there are a - // variety of complications. A call with a nocapture argument may still - // temporary store the nocapture argument's value in a temporary memory - // location if that memory location doesn't escape. Or it may pass a - // nocapture value to other functions as long as they don't capture it. - if (isEscapeSource(O1) && - AAQI.CI->isNotCapturedBeforeOrAt(O2, cast(O1))) - return AliasResult::NoAlias; - if (isEscapeSource(O2) && - AAQI.CI->isNotCapturedBeforeOrAt(O1, cast(O2))) - return AliasResult::NoAlias; - - return AliasResult::MayAlias; -} - -namespace { -// TODO: Consider moving this code to AliasAnalysis.h, to make it accessible to -// other alias analyses. -// TODO: TapirFnBehavior::View and TapirFnBehavior::Strand may be redundant. -enum class TapirFnBehavior : uint8_t { - None = 0, - Injective = 1, - Pure = 2, // including strand pure function in same strand - View = 4, - InjectiveOrPureOrView = Injective | Pure | View, - Strand = 8, // excluding strand pure function in same strand - Any = InjectiveOrPureOrView | Strand, -}; - -static const std::pair - TapirFnAttrTable[] = { - {Attribute::Injective, TapirFnBehavior::Injective}, - {Attribute::HyperView, TapirFnBehavior::View}, - {Attribute::StrandPure, TapirFnBehavior::Strand}, -}; +// Begin alias rules for Tapir. TODO: Move these to a separate pass. -static inline bool noTapirFnBehavior(const TapirFnBehavior TFB) { - return (static_cast(TFB) & - static_cast(TapirFnBehavior::Any)) == - static_cast(TapirFnBehavior::None); -} -static inline bool isInjectiveSet(const TapirFnBehavior TFB) { - return (static_cast(TFB) & - static_cast(TapirFnBehavior::Injective)) == - static_cast(TapirFnBehavior::Injective); -} -static inline bool isPureSet(const TapirFnBehavior TFB) { - return (static_cast(TFB) & - static_cast(TapirFnBehavior::Pure)) == - static_cast(TapirFnBehavior::Pure); -} -static inline bool isViewSet(const TapirFnBehavior TFB) { - return (static_cast(TFB) & - static_cast(TapirFnBehavior::View)) == - static_cast(TapirFnBehavior::View); -} -static inline bool isInjectiveOrPureOrViewSet(const TapirFnBehavior TFB) { - return static_cast(TFB) & - static_cast(TapirFnBehavior::InjectiveOrPureOrView); -} -static inline bool isStrandSet(const TapirFnBehavior TFB) { - return (static_cast(TFB) & - static_cast(TapirFnBehavior::Strand)) == - static_cast(TapirFnBehavior::Strand); -} -static inline TapirFnBehavior setPure(const TapirFnBehavior TFB) { - return TapirFnBehavior(static_cast(TFB) | - static_cast(TapirFnBehavior::Pure)); -} -static inline TapirFnBehavior clearPure(const TapirFnBehavior TFB) { - return TapirFnBehavior(static_cast(TFB) & - ~static_cast(TapirFnBehavior::Pure)); -} -static inline TapirFnBehavior clearStrand(const TapirFnBehavior TFB) { - return TapirFnBehavior(static_cast(TFB) & - ~static_cast(TapirFnBehavior::Strand)); -} -static inline TapirFnBehavior unionTapirFnBehavior(const TapirFnBehavior TFB1, - const TapirFnBehavior TFB2) { - return TapirFnBehavior(static_cast(TFB1) | - static_cast(TFB2)); -} -static inline TapirFnBehavior -intersectTapirFnBehavior(const TapirFnBehavior TFB1, - const TapirFnBehavior TFB2) { - return TapirFnBehavior(static_cast(TFB1) & - static_cast(TFB2)); -} -} // namespace - -// Tapir/OpenCilk code has some simple optimization opportunities. -// 1. Some runtime functions are injections, i.e., they return nonaliasing -// pointers when given nonaliasing arguments. -// 2. Some runtime functions are pure, or pure within a region of execution, -// which means the return values MustAlias if the arguments are identical. -// 3. View lookups return a value that does not alias anything that the -// argument does not alias (for simplicity, this implies injective). -// 4. Token lookups return a value that does not alias any alloca or global. -static const Value *getRecognizedArgument(const Value *V, bool InSameSpindle, - const Value *&Fn, - TapirFnBehavior &Behavior) { - const CallInst *C = dyn_cast(V); - if (!C) - return nullptr; - unsigned NumOperands = C->getNumOperands(); - if (NumOperands != 2 && NumOperands != 5) +// Return an argument with the HyperView attribute, or null. +// To avoid expensive checks this only returns identifiable objects +// (as defined by isIdentifiedObjects). +const Value * +BasicAAResult::getViewClass(const CallBase *Call, AAQueryInfo &AAQI) { + if (!Call) return nullptr; - for (auto E : TapirFnAttrTable) { - if (C->hasFnAttr(E.first)) - Behavior = unionTapirFnBehavior(Behavior, E.second); - } - - // Make TapirFnBehavior::Strand and TapirFnBehavior::Pure mutually exclusive. - if (isStrandSet(Behavior)) { - if (InSameSpindle) - Behavior = setPure(clearStrand(Behavior)); - else - Behavior = clearPure(Behavior); - } else if (C->doesNotAccessMemory() && C->doesNotThrow() && - C->hasFnAttr(Attribute::WillReturn)) { - Behavior = setPure(Behavior); + AttributeList Attrs = Call->getAttributes(); + unsigned NumOperands = Call->getNumOperands(); + for (unsigned I = 0; I < NumOperands; ++I) { + if (Attrs.hasParamAttr(I, Attribute::HyperView)) { + Value *Arg = Call->getOperand(I); + if (isIdentifiedObject(Arg)) + return Arg; + } } + return nullptr; +} - if (noTapirFnBehavior(Behavior)) - return nullptr; - Fn = C->getCalledOperand(); - return C->getOperand(0); +// This matches llvm.tapir.frame. +static bool isTapirFrame(const CallBase *Call) { + if (!isa(Call)) + return false; + return cast(Call)->getIntrinsicID() == Intrinsic::tapir_frame; } +// Find an argument with the Injective property. +// Two calls to the same function with different values of an argument +// with the Injective attribute return non-aliasing results. +// PartialAlias is never returned. AliasResult -BasicAAResult::checkInjectiveArguments(const Value *V1, const Value *O1, - const Value *V2, const Value *O2, +BasicAAResult::checkInjectiveArguments(const CallBase *C1, + const CallBase *C2, AAQueryInfo &AAQI) { - // V1 and V2 are the original pointers stripped of casts - // O1 and O2 are the underlying objects stripped of GEP as well - - const Value *Fn1 = nullptr, *Fn2 = nullptr; - TapirFnBehavior Behavior1 = TapirFnBehavior::None, - Behavior2 = TapirFnBehavior::None; - bool InSameSpindle = AAQI.AssumeSameSpindle; - const Value *A1 = getRecognizedArgument(V1, InSameSpindle, Fn1, Behavior1); - const Value *A2 = getRecognizedArgument(V2, InSameSpindle, Fn2, Behavior2); - - if (!isInjectiveOrPureOrViewSet(Behavior1) && - !isInjectiveOrPureOrViewSet(Behavior2)) + unsigned NumOperands = C1->getNumOperands(); + const Value *Fn = C1->getCalledOperand(); + if (C2->getNumOperands() != NumOperands || C2->getCalledOperand() != Fn) return AliasResult::MayAlias; - // At least one value is a call to an understood function - assert(A1 || A2); - assert(!!A1 == !!Fn1); - assert(!!A2 == !!Fn2); - - // Calls to two different functions can not be analyzed. - if (Fn1 && Fn2 && Fn1 != Fn2) - return AliasResult::MayAlias; + AttributeList Attrs = C1->getAttributes(); - // Pure functions return equal values given equal arguments. + // The injective arguments + const Value *IArg1 = nullptr, *IArg2 = nullptr; + // Is the function guaranteed to return the same value if the + // injective arguments are the same? + // TODO: If not strand pure, get the MemoryEffects for the call. AliasResult Equal = - isPureSet(intersectTapirFnBehavior(Behavior1, Behavior2)) ? - AliasResult::MustAlias : AliasResult::MayAlias; - - // This is for testing. The intended use is with pointer arguments. - if (A1 && A2 && isInjectiveSet(Behavior1)) { - if (const ConstantInt *I1 = dyn_cast(A1)) { - if (const ConstantInt *I2 = dyn_cast(A2)) - return I1->getValue() == I2->getValue() ? - Equal : AliasResult(AliasResult::NoAlias); - return AliasResult::MayAlias; + AAQI.AssumeSameSpindle && Attrs.hasFnAttr(Attribute::StrandPure) + ? AliasResult::MustAlias : AliasResult::MayAlias; + + // Record injective arguments. + for (unsigned I = 0; I < NumOperands; ++I) { + const Value *A1 = C1->getOperand(I); + const Value *A2 = C2->getOperand(I); + if (Attrs.hasParamAttr(I, Attribute::Injective)) { + if (IArg1) + return AliasResult::MayAlias; // allow only one injective argument + IArg1 = A1; + IArg2 = A2; + continue; } + if (Equal == AliasResult::MayAlias) + continue; + if (isValueEqualInPotentialCycles(A1, A2, AAQI)) + continue; + if (isa(A1) || isa(A2)) + continue; + // Calls to llvm.tapir.frame are equivalent. + if (isa(A1) && isTapirFrame(cast(A1)) && + isa(A2) && isTapirFrame(cast(A2))) + continue; + Equal = AliasResult::MayAlias; } - bool Known1 = false, Known2 = false; - const Value *U1 = nullptr, *U2 = nullptr; - - if (A1) { - U1 = getUnderlyingObject(A1, MaxLookupSearchDepth); - Known1 = isIdentifiedObject(U1); - } - if (A2) { - U2 = getUnderlyingObject(A2, MaxLookupSearchDepth); - Known2 = isIdentifiedObject(U2); - } - - // Rules, in order: - // 1. Potentially unequal values based on the same object may alias. - // 2. View lookups do not alias allocas that do not alias the argument - if (!A1) { - if (!Known2) - return AliasResult::MayAlias; - if (O1 == U2) // 1 - return AliasResult::MayAlias; - if (isViewSet(Behavior2)) // 2 - return UnderlyingNoAlias(O1, U2, AAQI); - return AliasResult::MayAlias; - } - if (!A2) { - if (!Known1) - return AliasResult::MayAlias; - if (U1 == O2) // 1 - return AliasResult::MayAlias; - if (isViewSet(Behavior1)) // 2 - return UnderlyingNoAlias(U1, O2, AAQI); - return AliasResult::MayAlias; - } + // No injective arguments. + if (IArg1 == nullptr || IArg2 == nullptr) + return Equal; - if (!isInjectiveSet(Behavior1)) + // aliasCheck returns NoAlias for types other than pointers. + if (!IArg1->getType()->isPointerTy() || !IArg2->getType()->isPointerTy()) return AliasResult::MayAlias; - // Two calls to the same function with the same value. - if (isValueEqualInPotentialCycles(A1, A2, AAQI)) + AliasResult ArgAlias = + aliasCheck(IArg1, LocationSize(1), + IArg2, LocationSize(1), + AAQI, nullptr); + if (ArgAlias == AliasResult::NoAlias) + return AliasResult::NoAlias; + if (ArgAlias == AliasResult::MustAlias) return Equal; + return AliasResult::MayAlias; +} + +AliasResult BasicAAResult::aliasCheckTapir(const Value *V1, const Value *V2, + LocationSize Size, + AAQueryInfo &AAQI, + const Instruction *CtxI) { + // aliasCheck has already returned MayAlias + + V1 = V1->stripPointerCastsForAliasAnalysis(); + V2 = V2->stripPointerCastsForAliasAnalysis(); - // Two calls with different values based on the same object. - if (U1 == U2) { - // TODO: Currently the caller only cares whether the result is NoAlias. - // If the caller relied on partial overlap detection a function like - // void *f(void *p) { return p; } - // could not be declared injective. - BasicAAResult::DecomposedGEP DecompGEP1 = - DecomposeGEPExpression(A1, DL, &AC, DT); - BasicAAResult::DecomposedGEP DecompGEP2 = - DecomposeGEPExpression(A2, DL, &AC, DT); - if (DecompGEP1.VarIndices.empty() && DecompGEP2.VarIndices.empty() && - isValueEqualInPotentialCycles(DecompGEP1.Base, DecompGEP2.Base, AAQI)) - return DecompGEP1.Offset == DecompGEP2.Offset - ? Equal - : AliasResult(AliasResult::NoAlias); + const CallBase *C1 = dyn_cast(V1); + const CallBase *C2 = dyn_cast(V2); + + if (!C1 && !C2) return AliasResult::MayAlias; - } - return UnderlyingNoAlias(U1, U2, AAQI); + if (C1 && C2 && + checkInjectiveArguments(C1, C2, AAQI) == AliasResult::NoAlias) + return AliasResult::NoAlias; + + const Value *VC1 = getViewClass(C1, AAQI); + const Value *VC2 = getViewClass(C2, AAQI); + + if (!VC1 && !VC2) + return AliasResult::MayAlias; + + if (!VC1) + VC1 = V1; + if (!VC2) + VC2 = V2; + + if (aliasCheck(VC1, Size, VC2, Size, AAQI, nullptr) == AliasResult::NoAlias) + return AliasResult::NoAlias; + + return AliasResult::MayAlias; } +// End alias rules for Tapir. + /// Provides a bunch of ad-hoc rules to disambiguate in common cases, such as /// array references. AliasResult BasicAAResult::aliasCheck(const Value *V1, LocationSize V1Size, @@ -1738,17 +1614,44 @@ AliasResult BasicAAResult::aliasCheck(const Value *V1, LocationSize V1Size, if (!NullPointerIsDefined(&F, CPN->getType()->getAddressSpace())) return AliasResult::NoAlias; - // If the call is an injection (distinct argument implies - // distinct return) some more optimization is possible. - AliasResult InjectiveResult = - checkInjectiveArguments(V1, O1, V2, O2, AAQI); - if (InjectiveResult == AliasResult::NoAlias) - return AliasResult::NoAlias; - else if (InjectiveResult == AliasResult::MustAlias) - return AliasResult::MayAlias; + if (O1 != O2) { + // If V1/V2 point to two different objects, we know that we have no alias. + if (AAQI.AssumeSameSpindle) { + if (isIdentifiedObjectIfInSameSpindle(O1) && + isIdentifiedObjectIfInSameSpindle(O2)) + return AliasResult::NoAlias; + } else { + if (isIdentifiedObject(O1) && isIdentifiedObject(O2)) + return AliasResult::NoAlias; + } - if (O1 != O2 && UnderlyingNoAlias(O1, O2, AAQI) == AliasResult::NoAlias) - return AliasResult::NoAlias; + // Constant pointers can't alias with non-const isIdentifiedObject objects. + if ((isa(O1) && isIdentifiedObject(O2) && !isa(O2)) || + (isa(O2) && isIdentifiedObject(O1) && !isa(O1))) + return AliasResult::NoAlias; + + // Function arguments can't alias with things that are known to be + // unambigously identified at the function level. + if ((isa(O1) && isIdentifiedFunctionLocal(O2)) || + (isa(O2) && isIdentifiedFunctionLocal(O1))) + return AliasResult::NoAlias; + + // If one pointer is the result of a call/invoke or load and the other is a + // non-escaping local object within the same function, then we know the + // object couldn't escape to a point where the call could return it. + // + // Note that if the pointers are in different functions, there are a + // variety of complications. A call with a nocapture argument may still + // temporary store the nocapture argument's value in a temporary memory + // location if that memory location doesn't escape. Or it may pass a + // nocapture value to other functions as long as they don't capture it. + if (isEscapeSource(O1) && + AAQI.CI->isNotCapturedBeforeOrAt(O2, cast(O1))) + return AliasResult::NoAlias; + if (isEscapeSource(O2) && + AAQI.CI->isNotCapturedBeforeOrAt(O1, cast(O2))) + return AliasResult::NoAlias; + } // If the size of one access is larger than the entire object on the other // side, then we know such behavior is undefined and can assume no alias. diff --git a/llvm/lib/CodeGen/IntrinsicLowering.cpp b/llvm/lib/CodeGen/IntrinsicLowering.cpp index 61920a0e04ab..ad9305faabba 100644 --- a/llvm/lib/CodeGen/IntrinsicLowering.cpp +++ b/llvm/lib/CodeGen/IntrinsicLowering.cpp @@ -236,6 +236,16 @@ void IntrinsicLowering::LowerIntrinsicCall(CallInst *CI) { report_fatal_error("Code generator does not support intrinsic function '"+ Callee->getName()+"'!"); + case Intrinsic::tapir_frame: + CI->replaceAllUsesWith(Constant::getNullValue(CI->getType())); + break; + + case Intrinsic::hyper_lookup: { + // hyper_lookup, if not replaced by now, returns its second argument + Value *V = CI->getArgOperand(1); + CI->replaceAllUsesWith(V); + break; + } case Intrinsic::expect: { // Just replace __builtin_expect(exp, c) with EXP. Value *V = CI->getArgOperand(0); diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index b5c559fb0d9a..afc23f653ff6 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -7602,6 +7602,15 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, TLI.getFrameIndexTy(DAG.getDataLayout()), getValue(I.getArgOperand(0)))); return; + case Intrinsic::hyper_lookup: + // Return the second argument. + setValue(&I, getValue(I.getArgOperand(1))); + return; + case Intrinsic::tapir_frame: + // Return null. + setValue(&I, + DAG.getConstant(0, sdl, TLI.getPointerTy(DAG.getDataLayout()))); + return; } } diff --git a/llvm/lib/Transforms/Tapir/LoweringUtils.cpp b/llvm/lib/Transforms/Tapir/LoweringUtils.cpp index 319083f81e1e..dd7883af9e50 100644 --- a/llvm/lib/Transforms/Tapir/LoweringUtils.cpp +++ b/llvm/lib/Transforms/Tapir/LoweringUtils.cpp @@ -1242,6 +1242,7 @@ bool TapirTarget::shouldProcessFunction(const Function &F) const { case Intrinsic::reducer_unregister: case Intrinsic::tapir_loop_grainsize: case Intrinsic::task_frameaddress: + case Intrinsic::tapir_frame: case Intrinsic::tapir_runtime_start: case Intrinsic::tapir_runtime_end: return true; diff --git a/llvm/lib/Transforms/Tapir/OpenCilkABI.cpp b/llvm/lib/Transforms/Tapir/OpenCilkABI.cpp index f60b202b755d..c13b0b2b3f43 100644 --- a/llvm/lib/Transforms/Tapir/OpenCilkABI.cpp +++ b/llvm/lib/Transforms/Tapir/OpenCilkABI.cpp @@ -17,6 +17,7 @@ #include "llvm/ADT/StringSet.h" #include "llvm/ADT/Statistic.h" #include "llvm/Analysis/AssumptionCache.h" +#include "llvm/Analysis/CFG.h" #include "llvm/Analysis/TapirTaskInfo.h" #include "llvm/IR/DebugInfo.h" #include "llvm/IR/DebugInfoMetadata.h" @@ -226,8 +227,8 @@ void OpenCilkABI::prepareModule() { FunctionType *Grainsize16FnTy = FunctionType::get(Int16Ty, {Int16Ty}, false); FunctionType *Grainsize32FnTy = FunctionType::get(Int32Ty, {Int32Ty}, false); FunctionType *Grainsize64FnTy = FunctionType::get(Int64Ty, {Int64Ty}, false); - FunctionType *LookupTy = FunctionType::get( - VoidPtrTy, {VoidPtrTy, Int64Ty, VoidPtrTy, VoidPtrTy}, false); + FunctionType *LookupTy = FunctionType::get(VoidPtrTy, + {StackFramePtrTy, VoidPtrTy, Int64Ty, VoidPtrTy, VoidPtrTy}, false); FunctionType *UnregTy = FunctionType::get(VoidTy, {VoidPtrTy}, false); FunctionType *Reg32Ty = FunctionType::get(VoidTy, {VoidPtrTy, Int32Ty, VoidPtrTy, @@ -262,7 +263,7 @@ void OpenCilkABI::prepareModule() { CilkRTSCilkForGrainsize32}, {"__cilkrts_cilk_for_grainsize_64", Grainsize64FnTy, CilkRTSCilkForGrainsize64}, - {"__cilkrts_reducer_lookup", LookupTy, CilkRTSReducerLookup}, + {"__cilkrts_reducer_lookup_in_frame", LookupTy, CilkRTSReducerLookup}, {"__cilkrts_reducer_register_32", Reg32Ty, CilkRTSReducerRegister32}, {"__cilkrts_reducer_register_64", Reg64Ty, CilkRTSReducerRegister64}, {"__cilkrts_reducer_unregister", UnregTy, CilkRTSReducerUnregister}, @@ -441,8 +442,8 @@ Value *OpenCilkABI::CreateStackFrame(Function &F) { } Value* OpenCilkABI::GetOrCreateCilkStackFrame(Function &F) { - if (DetachCtxToStackFrame.count(&F)) - return DetachCtxToStackFrame[&F]; + if (Value *Frame = DetachCtxToStackFrame.lookup(&F)) + return Frame; Value *SF = CreateStackFrame(F); DetachCtxToStackFrame[&F] = SF; @@ -641,8 +642,8 @@ Value *OpenCilkABI::lowerGrainsizeCall(CallInst *GrainsizeCall) { BasicBlock *OpenCilkABI::GetDefaultSyncLandingpad(Function &F, Value *SF, DebugLoc Loc) { // Return an existing default sync landingpad, if there is one. - if (DefaultSyncLandingpad.count(&F)) - return cast(DefaultSyncLandingpad[&F]); + if (Value *Sync = DefaultSyncLandingpad.lookup(&F)) + return cast(Sync); // Create a default cleanup landingpad block. LLVMContext &C = F.getContext(); @@ -1118,6 +1119,52 @@ LoopOutlineProcessor *OpenCilkABI::getLoopOutlineProcessor( return nullptr; } +Value *OpenCilkABI::getValidFrame(CallBase *FrameCall, DominatorTree &DT) { + Function *F = FrameCall->getFunction(); + if (Value *Frame = DetachCtxToStackFrame.lookup(F)) { + // Make sure a call to enter_frame dominates this get_frame call + // and no call to leave_frame has potentially been executed. + // Otherwise return a null pointer value to mean unknown. + // This is correct in most functions and conservative in + // complicated functions. + bool Initialized = false; + Value *Enter1 = CILKRTS_FUNC(enter_frame_helper).getCallee(); + Value *Enter2 = CILKRTS_FUNC(enter_frame).getCallee(); + Value *Leave1 = CILKRTS_FUNC(leave_frame_helper).getCallee(); + Value *Leave2 = CILKRTS_FUNC(leave_frame).getCallee(); + Value *Leave3 = CilkHelperEpilogue.getCallee(); + Value *Leave4 = CilkParentEpilogue.getCallee(); + for (User *U : Frame->users()) { + if (CallBase *C = dyn_cast(U)) { + Function *Fn = C->getCalledFunction(); + if (Fn == nullptr) // indirect function call + continue; + if (Fn == Enter1 || Fn == Enter2) { + if (!Initialized && DT.dominates(C, FrameCall)) + Initialized = true; + continue; + } + if (Fn == Leave1 || Fn == Leave2 | Fn == Leave3 | Fn == Leave4) { + // TODO: ...unless an enter_frame call definitely intervenes. + if (isPotentiallyReachable(C, FrameCall, nullptr, &DT, nullptr)) + return Constant::getNullValue(FrameCall->getType()); + continue; + } + } + } + if (Initialized) + return Frame; + } + return Constant::getNullValue(FrameCall->getType()); +} + +void OpenCilkABI::lowerFrameCall(CallBase *FrameCall, DominatorTree &DT) { + assert(FrameCall->data_operands_size() == 0); + Value *Frame = getValidFrame(FrameCall, DT); + FrameCall->replaceAllUsesWith(Frame); + FrameCall->eraseFromParent(); +} + void OpenCilkABI::lowerReducerOperation(CallBase *CI) { FunctionCallee Fn = nullptr; const Function *Called = CI->getCalledFunction(); diff --git a/llvm/lib/Transforms/Tapir/TapirToTarget.cpp b/llvm/lib/Transforms/Tapir/TapirToTarget.cpp index d632c7113af3..27dfb416a336 100644 --- a/llvm/lib/Transforms/Tapir/TapirToTarget.cpp +++ b/llvm/lib/Transforms/Tapir/TapirToTarget.cpp @@ -224,35 +224,45 @@ bool TapirToTargetImpl::processSimpleABI(Function &F, BasicBlock *TFEntry) { SmallVector TaskFrameAddrCalls; SmallVector TapirRTCalls; SmallVector ReducerOperations; + SmallVector TapirFrameCalls; for (BasicBlock &BB : F) { for (Instruction &I : BB) { - // Record calls to get Tapir-loop grainsizes. - if (IntrinsicInst *II = dyn_cast(&I)) - if (Intrinsic::tapir_loop_grainsize == II->getIntrinsicID()) + + // Record sync instructions in this function. + if (SyncInst *SI = dyn_cast(&I)) { + Syncs.push_back(SI); + continue; + } + + if (IntrinsicInst *II = dyn_cast(&I)) { + switch (II->getIntrinsicID()) { + case Intrinsic::tapir_loop_grainsize: GrainsizeCalls.push_back(II); + break; - // Record calls to task_frameaddr intrinsics. - if (IntrinsicInst *II = dyn_cast(&I)) - if (Intrinsic::task_frameaddress == II->getIntrinsicID()) + case Intrinsic::task_frameaddress: TaskFrameAddrCalls.push_back(II); + break; - // Record calls to tapir_runtime_start intrinsics. We rely on analyzing - // uses of these intrinsic calls to find calls to tapir_runtime_end. - if (IntrinsicInst *II = dyn_cast(&I)) - if (Intrinsic::tapir_runtime_start == II->getIntrinsicID()) + // Record calls to tapir_runtime_start intrinsics. + // We rely on analyzing uses of these intrinsic calls + // to find calls to tapir_runtime_end. + case Intrinsic::tapir_runtime_start: TapirRTCalls.push_back(II); - - // Record sync instructions in this function. - if (SyncInst *SI = dyn_cast(&I)) - Syncs.push_back(SI); - - if (!dyn_cast(&I)) + break; + + case Intrinsic::tapir_frame: + TapirFrameCalls.push_back(II); + break; + + case Intrinsic::hyper_lookup: + case Intrinsic::reducer_register: + case Intrinsic::reducer_unregister: + ReducerOperations.push_back(cast(&I)); + break; + } continue; - - if (isTapirIntrinsic(Intrinsic::hyper_lookup, &I, nullptr) || - isTapirIntrinsic(Intrinsic::reducer_register, &I, nullptr) || - isTapirIntrinsic(Intrinsic::reducer_unregister, &I, nullptr)) - ReducerOperations.push_back(cast(&I)); + } } } @@ -260,6 +270,12 @@ bool TapirToTargetImpl::processSimpleABI(Function &F, BasicBlock *TFEntry) { // helper functions generated by this process. bool Changed = false; + while (!TapirFrameCalls.empty()) { + CallInst *CI = TapirFrameCalls.pop_back_val(); + Target->lowerFrameCall(CI, GetDT(F)); + Changed = true; + } + // Lower calls to get Tapir-loop grainsizes. while (!GrainsizeCalls.empty()) { CallInst *GrainsizeCall = GrainsizeCalls.pop_back_val(); @@ -398,20 +414,22 @@ bool TapirToTargetImpl::processFunction( splitTaskFrameCreateBlocks(F, &OA.DT, &TI); TI.findTaskFrameTree(); - bool ChangedCFG = false; + bool Changed = false; { NamedRegionTimer NRT("TargetPreProcess", "Target preprocessing", TimerGroupName, TimerGroupDescription, TimePassesIsEnabled); - ChangedCFG = Target->preProcessFunction(F, TI); + Changed = Target->preProcessFunction(F, TI); } // end timed region // If we don't need to do outlining, then just handle the simple ABI. if (!Target->shouldDoOutlining(F)) { // Process the Tapir instructions in F directly. - if (!Target->processOrdinaryFunction(F, &F.getEntryBlock())) - processSimpleABI(F, &F.getEntryBlock()); - return ChangedCFG; + if (Target->processOrdinaryFunction(F, &F.getEntryBlock())) + return true; + if (processSimpleABI(F, &F.getEntryBlock())) + return true; + return Changed; } // Traverse the tasks in this function in post order. @@ -433,17 +451,19 @@ bool TapirToTargetImpl::processFunction( // helpers. for (Spindle *TF : AllTaskFrames) { if (isSpawningTaskFrame(TF) && !isSpawnedTaskFrame(TF)) - processSpawnerTaskFrame(TF, TFToOutline, OA, TI); + Changed |= processSpawnerTaskFrame(TF, TFToOutline, OA, TI); else if (isSpawnedTaskFrame(TF)) - processOutlinedTask(TF->getTaskFromTaskFrame(), TFToOutline, OA, TI); + Changed |= processOutlinedTask(TF->getTaskFromTaskFrame(), + TFToOutline, OA, TI); else - if (!Target->processOrdinaryFunction(*TFToOutline[TF].Outline, - TF->getEntry())) - processSimpleABI(*TFToOutline[TF].Outline, TF->getEntry()); + Changed |= + (Target->processOrdinaryFunction(*TFToOutline[TF].Outline, + TF->getEntry()) || + processSimpleABI(*TFToOutline[TF].Outline, TF->getEntry())); NewHelpers.push_back(TFToOutline[TF].Outline); } // Process the root task - processRootTask(F, TFToOutline, OA, TI); + Changed |= processRootTask(F, TFToOutline, OA, TI); { NamedRegionTimer NRT("TargetPostProcess", "Target postprocessing", @@ -469,7 +489,7 @@ bool TapirToTargetImpl::processFunction( } }); - return ChangedCFG || !NewHelpers.empty(); + return Changed || !NewHelpers.empty(); } bool TapirToTargetImpl::run() { diff --git a/llvm/test/Transforms/Tapir/157.ll b/llvm/test/Transforms/Tapir/157.ll index 09b1def9ff2a..b90a114721d6 100644 --- a/llvm/test/Transforms/Tapir/157.ll +++ b/llvm/test/Transforms/Tapir/157.ll @@ -35,7 +35,7 @@ pfor.cond: ; preds = %pfor.inc, %pfor.ph pfor.body: ; preds = %pfor.cond %2 = load i64, i64* %add.ptr, align 8, !tbaa !4 %3 = bitcast i64* %sum to i8* - %4 = call i8* @llvm.hyper.lookup(i8* nonnull %3, i64 8, i8* bitcast (void (i8*)* @zero to i8*), i8* bitcast (void (i8*, i8*)* @plus to i8*)) + %4 = call i8* @llvm.hyper.lookup(ptr null, i8* nonnull injective %3, i64 8, i8* bitcast (void (i8*)* @zero to i8*), i8* bitcast (void (i8*, i8*)* @plus to i8*)) %5 = bitcast i8* %4 to i64* %6 = load i64, i64* %5, align 8, !tbaa !4 %add = add nsw i64 %6, %2 @@ -52,7 +52,7 @@ pfor.cond.cleanup: ; preds = %pfor.inc pfor.end: ; preds = %entry, %pfor.cond.cleanup %7 = bitcast i64* %sum to i8* - %8 = call i8* @llvm.hyper.lookup(i8* nonnull %7, i64 8, i8* bitcast (void (i8*)* @zero to i8*), i8* bitcast (void (i8*, i8*)* @plus to i8*)) + %8 = call i8* @llvm.hyper.lookup(ptr null, i8* nonnull %7, i64 8, i8* bitcast (void (i8*)* @zero to i8*), i8* bitcast (void (i8*, i8*)* @plus to i8*)) %9 = bitcast i8* %8 to i64* %10 = load i64, i64* %9, align 8, !tbaa !4 %11 = bitcast i64* %sum to i8* @@ -91,8 +91,8 @@ declare void @llvm.reducer.register.i64(i8*, i64, i8*, i8*) #2 ; Function Attrs: argmemonly mustprogress nounwind willreturn declare token @llvm.syncregion.start() #3 -; Function Attrs: hyper_view inaccessiblememonly injective mustprogress nofree nounwind readonly strand_pure willreturn -declare i8* @llvm.hyper.lookup(i8*, i64, i8*, i8*) #4 +; Function Attrs: inaccessiblememonly mustprogress nofree nounwind readonly strand_pure willreturn +declare i8* @llvm.hyper.lookup(i8 *, i8*, i64, i8*, i8*) #4 ; Function Attrs: argmemonly mustprogress nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 @@ -104,7 +104,7 @@ attributes #0 = { nounwind uwtable "frame-pointer"="all" "min-legal-vector-width attributes #1 = { argmemonly mustprogress nofree nosync nounwind willreturn } attributes #2 = { inaccessiblememonly mustprogress nounwind reducer_register willreturn } attributes #3 = { argmemonly mustprogress nounwind willreturn } -attributes #4 = { hyper_view inaccessiblememonly injective mustprogress nofree nounwind readonly strand_pure willreturn } +attributes #4 = { inaccessiblememonly mustprogress nofree nounwind readonly strand_pure willreturn } attributes #5 = { inaccessiblememonly mustprogress nounwind reducer_unregister willreturn } attributes #6 = { nounwind } diff --git a/llvm/test/Transforms/Tapir/injective.ll b/llvm/test/Transforms/Tapir/injective.ll new file mode 100644 index 000000000000..d67ed1f634e0 --- /dev/null +++ b/llvm/test/Transforms/Tapir/injective.ll @@ -0,0 +1,75 @@ +; RUN: opt -O1 -S < %s | FileCheck %s +; ModuleID = 'injective.c' +; Check for alias analysis of hyperobject views and CSE of +; multiple calls to llvm.tapir.frame within a spindle. +source_filename = "injective.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-freebsd13.2" + +@x = external dso_local global i32, align 4 +@y = external dso_local global i32, align 4 + +; CHECK-LABEL: @alter +; Function Attrs: nounwind uwtable +define dso_local i32 @alter() #0 { +entry: +; One frame lookup remains. +; CHECK: %frame = notail call ptr @llvm.tapir.frame() + %frame = notail call ptr @llvm.tapir.frame() +; CHECK: %viewx = notail call ptr @llvm.hyper.lookup.i64(ptr %frame, ptr hyper_view injective @x, i64 4, ptr nonnull @zero, ptr nonnull @plus) + %viewx = notail call ptr @llvm.hyper.lookup.i64(ptr %frame, ptr hyper_view injective @x, i64 4, ptr nonnull @zero, ptr nonnull @plus) +; CHECK: %0 = load i32, ptr %viewx, align 4, !tbaa !4 + %0 = load i32, ptr %viewx, align 4, !tbaa !4 + %inc = add nsw i32 %0, 1 + store i32 %inc, ptr %viewx, align 4, !tbaa !4 + %1 = call ptr @llvm.tapir.frame() +; The frame lookups should have been CSE-ed. +; CHECK-NOT: call ptr @llvm.tapir.frame() +; CHECK: notail call ptr @llvm.hyper.lookup.i64(ptr %frame, ptr hyper_view injective @y, i64 4, ptr nonnull @zero, ptr nonnull @plus) +; CHECK-NOT: @llvm.tapir.frame() +; CHECK-NOT: @llvm.hyper.lookup.i64 + %viewy = notail call ptr @llvm.hyper.lookup.i64(ptr %1, ptr hyper_view injective @y, i64 4, ptr nonnull @zero, ptr nonnull @plus) +; CHECK: %yinit = load i32, ptr %viewy, align 4, !tbaa !4 + %yinit = load i32, ptr %viewy, align 4, !tbaa !4 +; CHECK-NOT: load +; CHECK-NOT: @llvm.tapir.frame() +; CHECK-NOT: @llvm.hyper.lookup.i64 + %inc1 = add nsw i32 %yinit, 1 + store i32 %inc1, ptr %viewy, align 4, !tbaa !4 + %2 = call ptr @llvm.tapir.frame() + %3 = notail call ptr @llvm.hyper.lookup.i64(ptr %2, ptr hyper_view injective @x, i64 4, ptr nonnull @zero, ptr nonnull @plus) + %4 = load i32, ptr %3, align 4, !tbaa !4 + %5 = call ptr @llvm.tapir.frame() + %6 = notail call ptr @llvm.hyper.lookup.i64(ptr %5, ptr hyper_view injective @y, i64 4, ptr nonnull @zero, ptr nonnull @plus) + %7 = load i32, ptr %6, align 4, !tbaa !4 + %add = add nsw i32 %4, %7 +; CHECK: ret i32 %add + ret i32 %add +} + +; Function Attrs: nounwind strand_pure willreturn memory(read) +declare ptr @llvm.tapir.frame() #1 + +; Function Attrs: nounwind strand_pure willreturn memory(inaccessiblemem: read) +declare ptr @llvm.hyper.lookup.i64(ptr, ptr align 1 injective, i64, ptr, ptr) #2 + +declare dso_local void @zero(ptr noundef) #3 + +declare dso_local void @plus(ptr noundef, ptr noundef) #3 + +attributes #0 = { nounwind uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #1 = { nounwind strand_pure willreturn memory(read) } +attributes #2 = { nounwind strand_pure willreturn memory(inaccessiblemem: read) } +attributes #3 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } + +!llvm.module.flags = !{!0, !1, !2} +!llvm.ident = !{!3} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 7, !"uwtable", i32 2} +!2 = !{i32 7, !"frame-pointer", i32 2} +!3 = !{!"clang version 16.0.6 (git@github.com:OpenCilk/opencilk-project.git 0ae760ffead5cd23cdd587da515c256f2c0570d6)"} +!4 = !{!5, !5, i64 0} +!5 = !{!"int", !6, i64 0} +!6 = !{!"omnipotent char", !7, i64 0} +!7 = !{!"Simple C/C++ TBAA"} diff --git a/llvm/test/Transforms/Tapir/loop-stripmine-epilog-taskframe.ll b/llvm/test/Transforms/Tapir/loop-stripmine-epilog-taskframe.ll index f19908c12833..71c3517d1ea0 100644 --- a/llvm/test/Transforms/Tapir/loop-stripmine-epilog-taskframe.ll +++ b/llvm/test/Transforms/Tapir/loop-stripmine-epilog-taskframe.ll @@ -212,13 +212,13 @@ det.achd: ; preds = %pfor.body %sum_racy.0.load = load i64, i64* %sum_racy, align 8 %add5 = add nsw i64 %sum_racy.0.load, %4 store i64 %add5, i64* %sum_racy, align 8, !tbaa !7 - %5 = call i8* @llvm.hyper.lookup.i64(i8* nonnull %0, i64 8, i8* bitcast (void (i8*)* @_ZN4cilkL4zeroIlEEvPv to i8*), i8* bitcast (void (i8*, i8*)* @_ZN4cilkL4plusIlEEvPvS1_ to i8*)) + %5 = call i8* @llvm.hyper.lookup.i64(ptr null, i8* nonnull injective %0, i64 8, i8* bitcast (void (i8*)* @_ZN4cilkL4zeroIlEEvPv to i8*), i8* bitcast (void (i8*, i8*)* @_ZN4cilkL4plusIlEEvPvS1_ to i8*)) %6 = bitcast i8* %5 to i64* %7 = load i64, i64* %6, align 8, !tbaa !7 %add7 = add nsw i64 %7, %4 store i64 %add7, i64* %6, align 8, !tbaa !7 %8 = load i64, i64* %add.ptr.i, align 8, !tbaa !7 - %9 = call i8* @llvm.hyper.lookup.i64(i8* nonnull %1, i64 8, i8* bitcast (void (i8*)* @_ZN4cilkL4zeroIlEEvPv to i8*), i8* bitcast (void (i8*, i8*)* @_ZN4cilkL4plusIlEEvPvS1_ to i8*)) + %9 = call i8* @llvm.hyper.lookup.i64(ptr null, i8* nonnull injective %1, i64 8, i8* bitcast (void (i8*)* @_ZN4cilkL4zeroIlEEvPv to i8*), i8* bitcast (void (i8*, i8*)* @_ZN4cilkL4plusIlEEvPvS1_ to i8*)) %10 = bitcast i8* %9 to i64* %11 = load i64, i64* %10, align 8, !tbaa !7 %sub9 = sub nsw i64 %11, %8 @@ -231,13 +231,13 @@ det.cont: ; preds = %det.achd, %pfor.bod %sum_racy.0.load91 = load i64, i64* %sum_racy, align 8 %add11 = add nsw i64 %sum_racy.0.load91, %12 store i64 %add11, i64* %sum_racy, align 8, !tbaa !7 - %13 = call i8* @llvm.hyper.lookup.i64(i8* nonnull %0, i64 8, i8* bitcast (void (i8*)* @_ZN4cilkL4zeroIlEEvPv to i8*), i8* bitcast (void (i8*, i8*)* @_ZN4cilkL4plusIlEEvPvS1_ to i8*)) + %13 = call i8* @llvm.hyper.lookup.i64(ptr null, i8* nonnull injective %0, i64 8, i8* bitcast (void (i8*)* @_ZN4cilkL4zeroIlEEvPv to i8*), i8* bitcast (void (i8*, i8*)* @_ZN4cilkL4plusIlEEvPvS1_ to i8*)) %14 = bitcast i8* %13 to i64* %15 = load i64, i64* %14, align 8, !tbaa !7 %add13 = add nsw i64 %15, %12 store i64 %add13, i64* %14, align 8, !tbaa !7 %16 = load i64, i64* %add.ptr.i79, align 8, !tbaa !7 - %17 = call i8* @llvm.hyper.lookup.i64(i8* nonnull %1, i64 8, i8* bitcast (void (i8*)* @_ZN4cilkL4zeroIlEEvPv to i8*), i8* bitcast (void (i8*, i8*)* @_ZN4cilkL4plusIlEEvPvS1_ to i8*)) + %17 = call i8* @llvm.hyper.lookup.i64(ptr null, i8* nonnull injective %1, i64 8, i8* bitcast (void (i8*)* @_ZN4cilkL4zeroIlEEvPv to i8*), i8* bitcast (void (i8*, i8*)* @_ZN4cilkL4plusIlEEvPvS1_ to i8*)) %18 = bitcast i8* %17 to i64* %19 = load i64, i64* %18, align 8, !tbaa !7 %sub15 = sub nsw i64 %19, %16 @@ -289,10 +289,10 @@ sync.continue21: ; preds = %pfor.cond.cleanup to label %cleanup unwind label %lpad17.loopexit.split-lp cleanup: ; preds = %sync.continue21, %entry - %21 = call i8* @llvm.hyper.lookup.i64(i8* nonnull %0, i64 8, i8* bitcast (void (i8*)* @_ZN4cilkL4zeroIlEEvPv to i8*), i8* bitcast (void (i8*, i8*)* @_ZN4cilkL4plusIlEEvPvS1_ to i8*)) + %21 = call i8* @llvm.hyper.lookup.i64(ptr null, i8* nonnull injective %0, i64 8, i8* bitcast (void (i8*)* @_ZN4cilkL4zeroIlEEvPv to i8*), i8* bitcast (void (i8*, i8*)* @_ZN4cilkL4plusIlEEvPvS1_ to i8*)) %22 = bitcast i8* %21 to i64* %23 = load i64, i64* %22, align 8, !tbaa !7 - %24 = call i8* @llvm.hyper.lookup.i64(i8* nonnull %1, i64 8, i8* bitcast (void (i8*)* @_ZN4cilkL4zeroIlEEvPv to i8*), i8* bitcast (void (i8*, i8*)* @_ZN4cilkL4plusIlEEvPvS1_ to i8*)) + %24 = call i8* @llvm.hyper.lookup.i64(ptr null, i8* nonnull injective %1, i64 8, i8* bitcast (void (i8*)* @_ZN4cilkL4zeroIlEEvPv to i8*), i8* bitcast (void (i8*, i8*)* @_ZN4cilkL4plusIlEEvPvS1_ to i8*)) %25 = bitcast i8* %24 to i64* %26 = load i64, i64* %25, align 8, !tbaa !7 %sum_racy.0.load92 = load i64, i64* %sum_racy, align 8 @@ -466,8 +466,8 @@ declare void @llvm.reducer.register.i64(i8*, i64, i8*, i8*) #11 ; Function Attrs: argmemonly mustprogress nounwind willreturn declare token @llvm.syncregion.start() #12 -; Function Attrs: hyper_view inaccessiblememonly injective mustprogress nofree nounwind readonly strand_pure willreturn -declare i8* @llvm.hyper.lookup.i64(i8*, i64, i8*, i8*) #13 +; Function Attrs: inaccessiblememonly mustprogress nofree nounwind readonly strand_pure willreturn +declare i8* @llvm.hyper.lookup.i64(i8*, i8*, i64, i8*, i8*) #13 ; Function Attrs: argmemonly mustprogress willreturn declare void @llvm.sync.unwind(token) #14 @@ -503,7 +503,7 @@ attributes #9 = { argmemonly mustprogress nofree norecurse nosync nounwind uwtab attributes #10 = { argmemonly mustprogress nofree norecurse nosync nounwind uwtable willreturn "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } attributes #11 = { inaccessiblememonly mustprogress nounwind reducer_register willreturn } attributes #12 = { argmemonly mustprogress nounwind willreturn } -attributes #13 = { hyper_view inaccessiblememonly injective mustprogress nofree nounwind readonly strand_pure willreturn } +attributes #13 = { inaccessiblememonly mustprogress nofree nounwind readonly strand_pure willreturn } attributes #14 = { argmemonly mustprogress willreturn } attributes #15 = { nofree nounwind "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } attributes #16 = { inaccessiblememonly mustprogress nounwind reducer_unregister willreturn } diff --git a/llvm/test/Transforms/Tapir/simple-loop-unswitch.ll b/llvm/test/Transforms/Tapir/simple-loop-unswitch.ll index a23f0b1af6fc..245845f607cb 100644 --- a/llvm/test/Transforms/Tapir/simple-loop-unswitch.ll +++ b/llvm/test/Transforms/Tapir/simple-loop-unswitch.ll @@ -16,20 +16,17 @@ declare token @llvm.syncregion.start() #2 ; Function Attrs: nofree nosync nounwind readnone speculatable willreturn declare double @llvm.fmuladd.f64(double, double, double) #3 -; Function Attrs: hyper_view inaccessiblememonly injective nounwind readonly strand_pure willreturn -declare i8* @llvm.hyper.lookup(i8*) #4 - ; Function Attrs: argmemonly nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #0 ; Function Attrs: argmemonly willreturn -declare void @llvm.detached.rethrow.sl_p0i8i32s(token, { i8*, i32 }) #5 +declare void @llvm.detached.rethrow.sl_p0i8i32s(token, { i8*, i32 }) #4 ; Function Attrs: argmemonly willreturn -declare void @llvm.sync.unwind(token) #5 +declare void @llvm.sync.unwind(token) #4 ; Function Attrs: inaccessiblememonly nounwind reducer_unregister willreturn -declare void @llvm.reducer.unregister(i8*) #6 +declare void @llvm.reducer.unregister(i8*) #5 define i32 @main(i1 %cmp.i) personality i32 (...)* undef { entry: @@ -149,6 +146,5 @@ attributes #0 = { argmemonly nofree nosync nounwind willreturn } attributes #1 = { inaccessiblememonly nounwind reducer_register willreturn } attributes #2 = { argmemonly nounwind willreturn } attributes #3 = { nofree nosync nounwind readnone speculatable willreturn } -attributes #4 = { hyper_view inaccessiblememonly injective nounwind readonly strand_pure willreturn } -attributes #5 = { argmemonly willreturn } -attributes #6 = { inaccessiblememonly nounwind reducer_unregister willreturn } +attributes #4 = { argmemonly willreturn } +attributes #5 = { inaccessiblememonly nounwind reducer_unregister willreturn } diff --git a/llvm/utils/TableGen/CodeGenIntrinsics.cpp b/llvm/utils/TableGen/CodeGenIntrinsics.cpp index 05330068533c..fa0a026d372c 100644 --- a/llvm/utils/TableGen/CodeGenIntrinsics.cpp +++ b/llvm/utils/TableGen/CodeGenIntrinsics.cpp @@ -78,7 +78,6 @@ CodeGenIntrinsic::CodeGenIntrinsic(Record *R, isInjective = false; isStrandPure = false; isHyperView = false; - isHyperToken = false; isReducerRegister = false; isReducerUnregister = false; @@ -182,6 +181,8 @@ void CodeGenIntrinsic::setProperty(Record *R) { ME &= MemoryEffects::argMemOnly(); else if (R->getName() == "IntrInaccessibleMemOnly") ME &= MemoryEffects::inaccessibleMemOnly(); + else if (R->getName() == "IntrReadInaccessibleMemOnly") + ME &= MemoryEffects::inaccessibleMemOnly(ModRefInfo::Ref); else if (R->getName() == "IntrInaccessibleMemOrArgMemOnly") ME &= MemoryEffects::inaccessibleOrArgMemOnly(); else if (R->getName() == "Commutative") @@ -220,8 +221,6 @@ void CodeGenIntrinsic::setProperty(Record *R) { isReducerRegister = true; else if (R->getName() == "IntrHyperView") isHyperView = true; - else if (R->getName() == "IntrHyperToken") - isHyperToken = true; else if (R->getName() == "IntrReducerUnregister") isReducerUnregister = true; else if (R->isSubClassOf("NoCapture")) { diff --git a/llvm/utils/TableGen/CodeGenIntrinsics.h b/llvm/utils/TableGen/CodeGenIntrinsics.h index 91fb20a97505..6a5317009bca 100644 --- a/llvm/utils/TableGen/CodeGenIntrinsics.h +++ b/llvm/utils/TableGen/CodeGenIntrinsics.h @@ -112,7 +112,6 @@ struct CodeGenIntrinsic { // Tapir reducer-related attributes bool isStrandPure; bool isHyperView; - bool isHyperToken; bool isReducerRegister; bool isReducerUnregister; diff --git a/llvm/utils/TableGen/IntrinsicEmitter.cpp b/llvm/utils/TableGen/IntrinsicEmitter.cpp index 58862ef2d523..1464ce149281 100644 --- a/llvm/utils/TableGen/IntrinsicEmitter.cpp +++ b/llvm/utils/TableGen/IntrinsicEmitter.cpp @@ -406,9 +406,6 @@ std::optional compareFnAttributes(const CodeGenIntrinsic *L, if (L->isHyperView != R->isHyperView) return R->isHyperView; - if (L->isHyperToken != R->isHyperToken) - return R->isHyperToken; - // Try to order by readonly/readnone attribute. uint32_t LK = L->ME.toIntValue(); uint32_t RK = R->ME.toIntValue(); @@ -555,8 +552,6 @@ void IntrinsicEmitter::EmitAttributes(const CodeGenIntrinsicTable &Ints, OS << " Attribute::get(C, Attribute::ReducerUnregister),\n"; if (Intrinsic.isHyperView) OS << " Attribute::get(C, Attribute::HyperView),\n"; - if (Intrinsic.isHyperToken) - OS << " Attribute::get(C, Attribute::HyperToken),\n"; MemoryEffects ME = Intrinsic.ME; // TODO: IntrHasSideEffects should affect not only readnone intrinsics. @@ -632,8 +627,7 @@ void IntrinsicEmitter::EmitAttributes(const CodeGenIntrinsicTable &Ints, Intrinsic.isConvergent || Intrinsic.isSpeculatable || Intrinsic.isStrictFP || Intrinsic.isInjective || Intrinsic.isStrandPure || Intrinsic.isReducerRegister || - Intrinsic.isReducerUnregister || Intrinsic.isHyperView || - Intrinsic.isHyperToken) { + Intrinsic.isReducerUnregister || Intrinsic.isHyperView) { unsigned ID = UniqFnAttributes.find(&Intrinsic)->second; OS << " AS[" << numAttrs++ << "] = {AttributeList::FunctionIndex, " << "getIntrinsicFnAttributeSet(C, " << ID << ")};\n";