已合并
fix: add local mode impl #606
songjian创建于 8月21日
fix: add local mode impl #606
已合并
共 35 个文件变更+1454-92
| @@ -63,10 +63,10 @@ void CangjieCompilerInstance::AddTyOfRefType(AST::RefType& rt) | |||
| 63 | decl = importManager->GetImportedDecl(COLLECTION_PACKAGE_NAME, rt.ref.identifier); | 63 | decl = importManager->GetImportedDecl(COLLECTION_PACKAGE_NAME, rt.ref.identifier); |
| 64 | } | 64 | } |
| 65 | } | 65 | } |
| 66 | - std::vector<Ptr<Ty>> typeArgs; | 66 | + std::vector<DataTy> typeArgs; |
| 67 | for (auto& arg : rt.typeArguments) { | 67 | for (auto& arg : rt.typeArguments) { |
| 68 | CheckTypeAndAddTy(arg); | 68 | CheckTypeAndAddTy(arg); |
| 69 | - typeArgs.push_back(arg->GetTy()); | 69 | + typeArgs.push_back(arg->DataTy()); |
| 70 | } | 70 | } |
| 71 | if (!decl) { | 71 | if (!decl) { |
| 72 | decl = rt.ref.target; | 72 | decl = rt.ref.target; |
| @@ -93,20 +93,21 @@ void CangjieCompilerInstance::CheckTypeAndAddTy(OwnedPtr<AST::Type>& type) | |||
| 93 | if (AST::Ty::IsTyCorrect(type->GetTy()) && !isGenericType) { | 93 | if (AST::Ty::IsTyCorrect(type->GetTy()) && !isGenericType) { |
| 94 | return; | 94 | return; |
| 95 | } | 95 | } |
| 96 | + auto mode = type->modal.ToModalInfo(); | ||
| 96 | if (type->astKind == AST::ASTKind::PRIMITIVE_TYPE) { | 97 | if (type->astKind == AST::ASTKind::PRIMITIVE_TYPE) { |
| 97 | auto pt = RawStaticCast<AST::PrimitiveType *>(type.get()); | 98 | auto pt = RawStaticCast<AST::PrimitiveType *>(type.get()); |
| 98 | - pt->SetTy(typeManager->GetPrimitiveTy(pt->kind)); | 99 | + pt->SetTy({typeManager->GetPrimitiveTy(pt->kind), mode}); |
| 99 | } else if (type->astKind == AST::ASTKind::REF_TYPE) { | 100 | } else if (type->astKind == AST::ASTKind::REF_TYPE) { |
| 100 | auto rt = RawStaticCast<AST::RefType *>(type.get()); | 101 | auto rt = RawStaticCast<AST::RefType *>(type.get()); |
| 101 | AddTyOfRefType(*rt); | 102 | AddTyOfRefType(*rt); |
| 102 | } else if (type->astKind == AST::ASTKind::TUPLE_TYPE) { | 103 | } else if (type->astKind == AST::ASTKind::TUPLE_TYPE) { |
| 103 | auto tt = RawStaticCast<AST::TupleType *>(type.get()); | 104 | auto tt = RawStaticCast<AST::TupleType *>(type.get()); |
| 104 | - std::vector<Ptr<Ty>> subTys; | 105 | + std::vector<DataTy> subTys; |
| 105 | for (auto& subType : tt->fieldTypes) { | 106 | for (auto& subType : tt->fieldTypes) { |
| 106 | CheckTypeAndAddTy(subType); | 107 | CheckTypeAndAddTy(subType); |
| 107 | - subTys.push_back(subType->GetTy()); | 108 | + subTys.push_back(subType->DataTy()); |
| 108 | } | 109 | } |
| 109 | - tt->SetTy(typeManager->GetTupleTy(subTys)); | 110 | + tt->SetTy({typeManager->GetTupleTy(subTys), mode}); |
| 110 | } else if (type->astKind == AST::ASTKind::VARRAY_TYPE) { | 111 | } else if (type->astKind == AST::ASTKind::VARRAY_TYPE) { |
| 111 | auto vaType = RawStaticCast<AST::VArrayType *>(type.get()); | 112 | auto vaType = RawStaticCast<AST::VArrayType *>(type.get()); |
| 112 | auto constType = RawStaticCast<AST::ConstantType *>(vaType->constantType.get()); | 113 | auto constType = RawStaticCast<AST::ConstantType *>(vaType->constantType.get()); |
| @@ -118,23 +119,23 @@ void CangjieCompilerInstance::CheckTypeAndAddTy(OwnedPtr<AST::Type>& type) | |||
| 118 | } | 119 | } |
| 119 | CheckTypeAndAddTy(vaType->typeArgument); | 120 | CheckTypeAndAddTy(vaType->typeArgument); |
| 120 | if (vaType->typeArgument) { | 121 | if (vaType->typeArgument) { |
| 121 | - vaType->SetTy(typeManager->GetVArrayTy(*vaType->typeArgument->GetTy(), vaSize)); | 122 | + vaType->SetTy({typeManager->GetVArrayTy(*vaType->typeArgument->GetTy(), vaSize), mode}); |
| 122 | } | 123 | } |
| 123 | } else if (type->astKind == AST::ASTKind::FUNC_TYPE) { | 124 | } else if (type->astKind == AST::ASTKind::FUNC_TYPE) { |
| 124 | auto func_type = RawStaticCast<AST::FuncType *>(type.get()); | 125 | auto func_type = RawStaticCast<AST::FuncType *>(type.get()); |
| 125 | - std::vector<Ptr<Cangjie::AST::Ty>> params; | 126 | + std::vector<ModalTy> params; |
| 126 | for (auto ¶m : func_type->paramTypes) { | 127 | for (auto ¶m : func_type->paramTypes) { |
| 127 | CheckTypeAndAddTy(param); | 128 | CheckTypeAndAddTy(param); |
| 128 | params.emplace_back(param->GetTy()); | 129 | params.emplace_back(param->GetTy()); |
| 129 | } | 130 | } |
| 130 | CheckTypeAndAddTy(func_type->retType); | 131 | CheckTypeAndAddTy(func_type->retType); |
| 131 | if (func_type->retType) { | 132 | if (func_type->retType) { |
| 132 | - type->SetTy(typeManager->GetFunctionTy(params, func_type->retType->GetTy())); | 133 | + type->SetTy({typeManager->GetFunctionTy(params, func_type->retType->GetTy()), mode}); |
| 133 | } | 134 | } |
| 134 | } | 135 | } |
| 135 | } | 136 | } |
| 136 | 137 | ||
| 137 | -Ptr<AST::Ty> CangjieCompilerInstance::GetInstantiatedTy(AST::ClassTy& cTy, Ptr<AST::Ty> paramTy) { | 138 | +DataTy CangjieCompilerInstance::GetInstantiatedTy(AST::ClassTy& cTy, DataTy paramTy) { |
| 138 | if (!paramTy->IsGeneric()) { | 139 | if (!paramTy->IsGeneric()) { |
| 139 | return paramTy; | 140 | return paramTy; |
| 140 | } | 141 | } |
| @@ -142,7 +143,7 @@ Ptr<AST::Ty> CangjieCompilerInstance::GetInstantiatedTy(AST::ClassTy& cTy, Ptr<A | |||
| 142 | CJC_ASSERT(typeParams.size() == cTy.typeArgs.size()); | 143 | CJC_ASSERT(typeParams.size() == cTy.typeArgs.size()); |
| 143 | for (size_t i = 0; i < typeParams.size(); i++) { | 144 | for (size_t i = 0; i < typeParams.size(); i++) { |
| 144 | if (typeParams[i]->identifier.Val() == paramTy->name) { | 145 | if (typeParams[i]->identifier.Val() == paramTy->name) { |
| 145 | - return cTy.typeArgs[i]; | 146 | + return cTy.TyArg(i); |
| 146 | } | 147 | } |
| 147 | } | 148 | } |
| 148 | return paramTy; | 149 | return paramTy; |
| @@ -157,7 +158,7 @@ OwnedPtr<AST::CallExpr> CangjieCompilerInstance::CreateSuperCall(AST::ClassDecl& | |||
| 157 | if (!superType) { | 158 | if (!superType) { |
| 158 | return nullptr; | 159 | return nullptr; |
| 159 | } | 160 | } |
| 160 | - auto cTy = StaticCast<AST::ClassTy *>(superType->GetTy()); | 161 | + auto cTy = StaticCast<AST::ClassTy *>(superType->DataTy()); |
| 161 | auto superExpr = CreateRefExpr("super"); | 162 | auto superExpr = CreateRefExpr("super"); |
| 162 | superExpr->isSuper = true; | 163 | superExpr->isSuper = true; |
| 163 | std::vector<OwnedPtr<FuncArg>> args; | 164 | std::vector<OwnedPtr<FuncArg>> args; |
| @@ -166,11 +167,11 @@ OwnedPtr<AST::CallExpr> CangjieCompilerInstance::CreateSuperCall(AST::ClassDecl& | |||
| 166 | superExpr->ref.target = fd; | 167 | superExpr->ref.target = fd; |
| 167 | superExpr->SetTy(fd->GetTy()); | 168 | superExpr->SetTy(fd->GetTy()); |
| 168 | for (auto& param : fd->funcBody->paramLists[0]->params) { | 169 | for (auto& param : fd->funcBody->paramLists[0]->params) { |
| 169 | - if (!param || !param->GetTy() || param->GetTy()->IsInvalid()) { | 170 | + if (!param || !param->DataTy() || param->GetTy()->IsInvalid()) { |
| 170 | continue; | 171 | continue; |
| 171 | } | 172 | } |
| 172 | // Parent's init func may have generic param. | 173 | // Parent's init func may have generic param. |
| 173 | - auto argTy = GetInstantiatedTy(*cTy, param->GetTy()); | 174 | + auto argTy = GetInstantiatedTy(*cTy, param->DataTy()); |
| 174 | auto initializer = CreateInitializer(argTy); | 175 | auto initializer = CreateInitializer(argTy); |
| 175 | if (!initializer) { | 176 | if (!initializer) { |
| 176 | return nullptr; | 177 | return nullptr; |
| @@ -198,10 +199,10 @@ void CangjieCompilerInstance::CreateInitFunc(AST::Decl& decl) | |||
| 198 | auto funcParamList = MakeOwned<Cangjie::AST::FuncParamList>(); | 199 | auto funcParamList = MakeOwned<Cangjie::AST::FuncParamList>(); |
| 199 | funcBody->paramLists.push_back(std::move(funcParamList)); | 200 | funcBody->paramLists.push_back(std::move(funcParamList)); |
| 200 | funcBody->body = MakeOwned<Cangjie::AST::Block>(); | 201 | funcBody->body = MakeOwned<Cangjie::AST::Block>(); |
| 201 | - funcBody->SetTy(funcTy); | 202 | + funcBody->SetTy({funcTy}); |
| 202 | 203 | ||
| 203 | auto initFunc = MakeOwned<Cangjie::AST::FuncDecl>(); | 204 | auto initFunc = MakeOwned<Cangjie::AST::FuncDecl>(); |
| 204 | - initFunc->SetTy(funcTy); | 205 | + initFunc->SetTy({funcTy}); |
| 205 | initFunc->funcBody = std::move(funcBody); | 206 | initFunc->funcBody = std::move(funcBody); |
| 206 | initFunc->funcBody->funcDecl = initFunc.get(); | 207 | initFunc->funcBody->funcDecl = initFunc.get(); |
| 207 | initFunc->identifier = "init"; | 208 | initFunc->identifier = "init"; |
| @@ -256,7 +257,7 @@ void CangjieCompilerInstance::UpdateDeclTyByGeneric(Ptr<AST::Decl> decl) { | |||
| 256 | for (auto& it : generic->typeParameters) { | 257 | for (auto& it : generic->typeParameters) { |
| 257 | auto gpd = RawStaticCast<AST::GenericParamDecl *>(it.get()); | 258 | auto gpd = RawStaticCast<AST::GenericParamDecl *>(it.get()); |
| 258 | CreateTyAndDefaultCtor(*gpd, {}); | 259 | CreateTyAndDefaultCtor(*gpd, {}); |
| 259 | - typeArgs.push_back(gpd->GetTy()); | 260 | + typeArgs.push_back(gpd->DataTy()); |
| 260 | } | 261 | } |
| 261 | for (auto& it : generic->genericConstraints) { | 262 | for (auto& it : generic->genericConstraints) { |
| 262 | auto gc = RawStaticCast<AST::GenericConstraint *>(it.get()); | 263 | auto gc = RawStaticCast<AST::GenericConstraint *>(it.get()); |
| @@ -312,7 +313,7 @@ Ptr<Ty> CangjieCompilerInstance::GetTyFromASTType(Decl& decl, const std::vector< | |||
| 312 | return typeManager->GetGenericsTy(*gpd); | 313 | return typeManager->GetGenericsTy(*gpd); |
| 313 | } | 314 | } |
| 314 | default: | 315 | default: |
| 315 | - return decl.GetTy(); | 316 | + return decl.DataTy(); |
| 316 | } | 317 | } |
| 317 | } | 318 | } |
| 318 | 319 | ||
| @@ -374,7 +375,7 @@ void CangjieCompilerInstance::CreateFuncdeclTy(AST::FuncDecl& fd) | |||
| 374 | return; | 375 | return; |
| 375 | } | 376 | } |
| 376 | auto retTy = fd.funcBody->retType->GetTy(); | 377 | auto retTy = fd.funcBody->retType->GetTy(); |
| 377 | - std::vector<Ptr<Cangjie::AST::Ty>> params; | 378 | + std::vector<ModalTy> params; |
| 378 | for (auto& param : fd.funcBody->paramLists[0]->params) { | 379 | for (auto& param : fd.funcBody->paramLists[0]->params) { |
| 379 | if (!param->type) { | 380 | if (!param->type) { |
| 380 | continue; | 381 | continue; |
| @@ -384,7 +385,7 @@ void CangjieCompilerInstance::CreateFuncdeclTy(AST::FuncDecl& fd) | |||
| 384 | params.emplace_back(param->GetTy()); | 385 | params.emplace_back(param->GetTy()); |
| 385 | } | 386 | } |
| 386 | UpdateDeclTyByGeneric(&fd); | 387 | UpdateDeclTyByGeneric(&fd); |
| 387 | - fd.SetTy(typeManager->GetFunctionTy(params, retTy)); | 388 | + fd.SetTy({typeManager->GetFunctionTy(params, retTy)}); |
| 388 | fd.funcBody->SetTy(fd.GetTy()); | 389 | fd.funcBody->SetTy(fd.GetTy()); |
| 389 | } | 390 | } |
| 390 | 391 | ||
| @@ -403,14 +404,14 @@ void CangjieCompilerInstance::CreateTyAndDefaultCtor( | |||
| 403 | CreateTyAndDefaultCtor(*rt->ref.target, {}); | 404 | CreateTyAndDefaultCtor(*rt->ref.target, {}); |
| 404 | } | 405 | } |
| 405 | } | 406 | } |
| 406 | - decl.SetTy(typeManager->GetClassTy(*cd, typeArgs)); | 407 | + decl.SetTy({typeManager->GetClassTy(*cd, typeArgs)}); |
| 407 | UpdateDeclTyByGeneric(&decl); | 408 | UpdateDeclTyByGeneric(&decl); |
| 408 | CreateDefaultCtor(decl); | 409 | CreateDefaultCtor(decl); |
| 409 | break; | 410 | break; |
| 410 | } | 411 | } |
| 411 | case ASTKind::INTERFACE_DECL: { | 412 | case ASTKind::INTERFACE_DECL: { |
| 412 | auto id = As<ASTKind::INTERFACE_DECL>(&decl); | 413 | auto id = As<ASTKind::INTERFACE_DECL>(&decl); |
| 413 | - decl.SetTy(typeManager->GetInterfaceTy(*id, typeArgs)); | 414 | + decl.SetTy({typeManager->GetInterfaceTy(*id, typeArgs)}); |
| 414 | UpdateDeclTyByGeneric(&decl); | 415 | UpdateDeclTyByGeneric(&decl); |
| 415 | break; | 416 | break; |
| 416 | } | 417 | } |
| @@ -425,14 +426,14 @@ void CangjieCompilerInstance::CreateTyAndDefaultCtor( | |||
| 425 | CreateTyAndDefaultCtor(*rt->ref.target, {}); | 426 | CreateTyAndDefaultCtor(*rt->ref.target, {}); |
| 426 | } | 427 | } |
| 427 | } | 428 | } |
| 428 | - decl.SetTy(typeManager->GetStructTy(*sd, typeArgs)); | 429 | + decl.SetTy({typeManager->GetStructTy(*sd, typeArgs)}); |
| 429 | UpdateDeclTyByGeneric(&decl); | 430 | UpdateDeclTyByGeneric(&decl); |
| 430 | CreateDefaultCtor(decl); | 431 | CreateDefaultCtor(decl); |
| 431 | break; | 432 | break; |
| 432 | } | 433 | } |
| 433 | case ASTKind::ENUM_DECL: { | 434 | case ASTKind::ENUM_DECL: { |
| 434 | auto ed = As<ASTKind::ENUM_DECL>(&decl); | 435 | auto ed = As<ASTKind::ENUM_DECL>(&decl); |
| 435 | - decl.SetTy(typeManager->GetEnumTy(*ed, typeArgs)); | 436 | + decl.SetTy({typeManager->GetEnumTy(*ed, typeArgs)}); |
| 436 | UpdateDeclTyByGeneric(&decl); | 437 | UpdateDeclTyByGeneric(&decl); |
| 437 | for (size_t i = 0; i < ed->constructors.size(); i++) { | 438 | for (size_t i = 0; i < ed->constructors.size(); i++) { |
| 438 | auto tempDecl = RawStaticCast<AST::Decl *>(ed->constructors[i].get()); | 439 | auto tempDecl = RawStaticCast<AST::Decl *>(ed->constructors[i].get()); |
| @@ -442,12 +443,12 @@ void CangjieCompilerInstance::CreateTyAndDefaultCtor( | |||
| 442 | } | 443 | } |
| 443 | case ASTKind::TYPE_ALIAS_DECL: { | 444 | case ASTKind::TYPE_ALIAS_DECL: { |
| 444 | auto tad = As<ASTKind::TYPE_ALIAS_DECL>(&decl); | 445 | auto tad = As<ASTKind::TYPE_ALIAS_DECL>(&decl); |
| 445 | - decl.SetTy(typeManager->GetTypeAliasTy(*tad, typeArgs)); | 446 | + decl.SetTy({typeManager->GetTypeAliasTy(*tad, typeArgs)}); |
| 446 | break; | 447 | break; |
| 447 | } | 448 | } |
| 448 | case ASTKind::GENERIC_PARAM_DECL: { | 449 | case ASTKind::GENERIC_PARAM_DECL: { |
| 449 | auto gpd = As<ASTKind::GENERIC_PARAM_DECL>(&decl); | 450 | auto gpd = As<ASTKind::GENERIC_PARAM_DECL>(&decl); |
| 450 | - decl.SetTy(typeManager->GetGenericsTy(*gpd)); | 451 | + decl.SetTy({typeManager->GetGenericsTy(*gpd)}); |
| 451 | break; | 452 | break; |
| 452 | } | 453 | } |
| 453 | case ASTKind::FUNC_DECL: { | 454 | case ASTKind::FUNC_DECL: { |
| @@ -943,7 +944,7 @@ void CangjieCompilerInstance::AddImportSpecToLLDBExprPackage() { | |||
| 943 | } | 944 | } |
| 944 | } | 945 | } |
| 945 | 946 | ||
| 946 | -OwnedPtr<Cangjie::AST::Expr> CangjieCompilerInstance::CreatePrimitiveInitializer(Ptr<Cangjie::AST::Ty> ty) | 947 | +OwnedPtr<Cangjie::AST::Expr> CangjieCompilerInstance::CreatePrimitiveInitializer(ModalTy ty) |
| 947 | { | 948 | { |
| 948 | switch (ty->kind) { | 949 | switch (ty->kind) { |
| 949 | case Cangjie::AST::TypeKind::TYPE_INT8: | 950 | case Cangjie::AST::TypeKind::TYPE_INT8: |
| @@ -972,9 +973,9 @@ OwnedPtr<Cangjie::AST::Expr> CangjieCompilerInstance::CreatePrimitiveInitializer | |||
| 972 | } | 973 | } |
| 973 | } | 974 | } |
| 974 | 975 | ||
| 975 | -OwnedPtr<Cangjie::AST::Expr> CangjieCompilerInstance::CreateEnumInitializer(Ptr<AST::Ty> ty) | 976 | +OwnedPtr<Cangjie::AST::Expr> CangjieCompilerInstance::CreateEnumInitializer(ModalTy ty) |
| 976 | { | 977 | { |
| 977 | - auto enumTy = RawStaticCast<AST::EnumTy *>(ty); | 978 | + auto enumTy = RawStaticCast<AST::EnumTy *>(ty.Ty()); |
| 978 | if (!enumTy) { | 979 | if (!enumTy) { |
| 979 | return nullptr; | 980 | return nullptr; |
| 980 | } | 981 | } |
| @@ -1005,7 +1006,7 @@ OwnedPtr<Cangjie::AST::Expr> CangjieCompilerInstance::CreateEnumInitializer(Ptr< | |||
| 1005 | } | 1006 | } |
| 1006 | 1007 | ||
| 1007 | Ptr<AST::FuncDecl> CangjieCompilerInstance::GetInitFuncFromClassOrStruct( | 1008 | Ptr<AST::FuncDecl> CangjieCompilerInstance::GetInitFuncFromClassOrStruct( |
| 1008 | - std::vector<OwnedPtr<Decl>>& decls, std::vector<Ptr<Cangjie::AST::Ty>>& params) | 1009 | + std::vector<OwnedPtr<Decl>>& decls, std::vector<ModalTy>& params) |
| 1009 | { | 1010 | { |
| 1010 | for (auto& decl : decls) { | 1011 | for (auto& decl : decls) { |
| 1011 | if (decl->TestAttr(Attribute::CONSTRUCTOR)) { | 1012 | if (decl->TestAttr(Attribute::CONSTRUCTOR)) { |
| @@ -1024,14 +1025,14 @@ Ptr<AST::FuncDecl> CangjieCompilerInstance::GetInitFuncFromClassOrStruct( | |||
| 1024 | return nullptr; | 1025 | return nullptr; |
| 1025 | } | 1026 | } |
| 1026 | 1027 | ||
| 1027 | -OwnedPtr<Cangjie::AST::Expr> CangjieCompilerInstance::CreateClassInitializer(Ptr<AST::Ty> ty) | 1028 | +OwnedPtr<Cangjie::AST::Expr> CangjieCompilerInstance::CreateClassInitializer(ModalTy ty) |
| 1028 | { | 1029 | { |
| 1029 | - auto classTy = RawStaticCast<AST::ClassTy *>(ty); | 1030 | + auto classTy = RawStaticCast<AST::ClassTy *>(ty.Ty()); |
| 1030 | if (!classTy || !classTy->decl) { | 1031 | if (!classTy || !classTy->decl) { |
| 1031 | return nullptr; | 1032 | return nullptr; |
| 1032 | } | 1033 | } |
| 1033 | auto cd = classTy->decl; | 1034 | auto cd = classTy->decl; |
| 1034 | - std::vector<Ptr<Cangjie::AST::Ty>> params; | 1035 | + std::vector<ModalTy> params; |
| 1035 | auto initFunc = GetInitFuncFromClassOrStruct(cd->body->decls, params); | 1036 | auto initFunc = GetInitFuncFromClassOrStruct(cd->body->decls, params); |
| 1036 | if (!initFunc) { | 1037 | if (!initFunc) { |
| 1037 | return nullptr; | 1038 | return nullptr; |
| @@ -1042,20 +1043,20 @@ OwnedPtr<Cangjie::AST::Expr> CangjieCompilerInstance::CreateClassInitializer(Ptr | |||
| 1042 | } | 1043 | } |
| 1043 | auto refExpr = CreateRefExpr(*initFunc); | 1044 | auto refExpr = CreateRefExpr(*initFunc); |
| 1044 | refExpr->ref.identifier = cd->identifier; | 1045 | refExpr->ref.identifier = cd->identifier; |
| 1045 | - refExpr->SetTy(typeManager->GetFunctionTy(params, classTy)); | 1046 | + refExpr->SetTy({typeManager->GetFunctionTy(params, ty)}); |
| 1046 | - auto call = AST::CreateCallExpr(std::move(refExpr), std::move(args), initFunc, classTy); | 1047 | + auto call = AST::CreateCallExpr(std::move(refExpr), std::move(args), initFunc, ty); |
| 1047 | call->callKind = CallKind::CALL_OBJECT_CREATION; | 1048 | call->callKind = CallKind::CALL_OBJECT_CREATION; |
| 1048 | return call; | 1049 | return call; |
| 1049 | } | 1050 | } |
| 1050 | 1051 | ||
| 1051 | -OwnedPtr<Cangjie::AST::Expr> CangjieCompilerInstance::CreateStructInitializer(Ptr<AST::Ty> ty) | 1052 | +OwnedPtr<Cangjie::AST::Expr> CangjieCompilerInstance::CreateStructInitializer(ModalTy ty) |
| 1052 | { | 1053 | { |
| 1053 | - auto structTy = RawStaticCast<AST::StructTy *>(ty); | 1054 | + auto structTy = RawStaticCast<AST::StructTy *>(ty.Ty()); |
| 1054 | if (!structTy || !structTy->decl) { | 1055 | if (!structTy || !structTy->decl) { |
| 1055 | return nullptr; | 1056 | return nullptr; |
| 1056 | } | 1057 | } |
| 1057 | auto sd = structTy->decl; | 1058 | auto sd = structTy->decl; |
| 1058 | - std::vector<Ptr<Cangjie::AST::Ty>> sparams; | 1059 | + std::vector<ModalTy> sparams; |
| 1059 | auto initFunc = GetInitFuncFromClassOrStruct(sd->body->decls, sparams); | 1060 | auto initFunc = GetInitFuncFromClassOrStruct(sd->body->decls, sparams); |
| 1060 | if (!initFunc) { | 1061 | if (!initFunc) { |
| 1061 | return nullptr; | 1062 | return nullptr; |
| @@ -1066,16 +1067,16 @@ OwnedPtr<Cangjie::AST::Expr> CangjieCompilerInstance::CreateStructInitializer(Pt | |||
| 1066 | } | 1067 | } |
| 1067 | auto refExpr = CreateRefExpr(*initFunc); | 1068 | auto refExpr = CreateRefExpr(*initFunc); |
| 1068 | refExpr->ref.identifier = sd->identifier; | 1069 | refExpr->ref.identifier = sd->identifier; |
| 1069 | - refExpr->SetTy(typeManager->GetFunctionTy(sparams, structTy)); | 1070 | + refExpr->SetTy({typeManager->GetFunctionTy(sparams, ty)}); |
| 1070 | - auto call = AST::CreateCallExpr(std::move(refExpr), std::move(args), initFunc, structTy); | 1071 | + auto call = AST::CreateCallExpr(std::move(refExpr), std::move(args), initFunc, ty); |
| 1071 | call->callKind = CallKind::CALL_STRUCT_CREATION; | 1072 | call->callKind = CallKind::CALL_STRUCT_CREATION; |
| 1072 | return call; | 1073 | return call; |
| 1073 | } | 1074 | } |
| 1074 | 1075 | ||
| 1075 | -OwnedPtr<Cangjie::AST::Expr> CangjieCompilerInstance::CreateFunctionInitializer(Ptr<AST::Ty> ty) | 1076 | +OwnedPtr<Cangjie::AST::Expr> CangjieCompilerInstance::CreateFunctionInitializer(ModalTy ty) |
| 1076 | { | 1077 | { |
| 1077 | // var result: (Int8) -> Int8 = {a: Int8 => 0 } | 1078 | // var result: (Int8) -> Int8 = {a: Int8 => 0 } |
| 1078 | - auto functy = RawStaticCast<AST::FuncTy*>(ty); | 1079 | + auto functy = RawStaticCast<AST::FuncTy*>(ty.Ty()); |
| 1079 | if (!functy) { | 1080 | if (!functy) { |
| 1080 | return nullptr; | 1081 | return nullptr; |
| 1081 | } | 1082 | } |
| @@ -1096,7 +1097,7 @@ OwnedPtr<Cangjie::AST::Expr> CangjieCompilerInstance::CreateFunctionInitializer( | |||
| 1096 | return AST::CreateLambdaExpr(std::move(funcBody)); | 1097 | return AST::CreateLambdaExpr(std::move(funcBody)); |
| 1097 | } | 1098 | } |
| 1098 | 1099 | ||
| 1099 | -OwnedPtr<Cangjie::AST::Expr> CangjieCompilerInstance::CreateRangeInitializer(Ptr<AST::Ty> ty) | 1100 | +OwnedPtr<Cangjie::AST::Expr> CangjieCompilerInstance::CreateRangeInitializer(ModalTy ty) |
| 1100 | { | 1101 | { |
| 1101 | if (ty->typeArgs.empty()) { | 1102 | if (ty->typeArgs.empty()) { |
| 1102 | return nullptr; | 1103 | return nullptr; |
| @@ -1105,13 +1106,13 @@ OwnedPtr<Cangjie::AST::Expr> CangjieCompilerInstance::CreateRangeInitializer(Ptr | |||
| 1105 | rangeExpr->startExpr = CreateInitializer(ty->typeArgs[0]); | 1106 | rangeExpr->startExpr = CreateInitializer(ty->typeArgs[0]); |
| 1106 | rangeExpr->stopExpr = CreateInitializer(ty->typeArgs[0]); | 1107 | rangeExpr->stopExpr = CreateInitializer(ty->typeArgs[0]); |
| 1107 | auto int64ty = typeManager->GetPrimitiveTy(AST::TypeKind::TYPE_INT64); | 1108 | auto int64ty = typeManager->GetPrimitiveTy(AST::TypeKind::TYPE_INT64); |
| 1108 | - rangeExpr->stepExpr = AST::CreateLitConstExpr(Cangjie::AST::LitConstKind::INTEGER, "1", int64ty); | 1109 | + rangeExpr->stepExpr = AST::CreateLitConstExpr(Cangjie::AST::LitConstKind::INTEGER, "1", {int64ty}); |
| 1109 | rangeExpr->SetTy(ty); | 1110 | rangeExpr->SetTy(ty); |
| 1110 | rangeExpr->decl = importManager->GetCoreDecl<Cangjie::AST::StructDecl>(RANGE_NAME); | 1111 | rangeExpr->decl = importManager->GetCoreDecl<Cangjie::AST::StructDecl>(RANGE_NAME); |
| 1111 | return rangeExpr; | 1112 | return rangeExpr; |
| 1112 | } | 1113 | } |
| 1113 | 1114 | ||
| 1114 | -OwnedPtr<Cangjie::AST::Expr> CangjieCompilerInstance::CreateInitializer(Ptr<Cangjie::AST::Ty> ty) | 1115 | +OwnedPtr<Cangjie::AST::Expr> CangjieCompilerInstance::CreateInitializer(ModalTy ty) |
| 1115 | { | 1116 | { |
| 1116 | if (!ty) { | 1117 | if (!ty) { |
| 1117 | return nullptr; | 1118 | return nullptr; |
| @@ -1145,7 +1146,7 @@ OwnedPtr<Cangjie::AST::Expr> CangjieCompilerInstance::CreateInitializer(Ptr<Cang | |||
| 1145 | } | 1146 | } |
| 1146 | if (ty->kind == Cangjie::AST::TypeKind::TYPE_VARRAY) { | 1147 | if (ty->kind == Cangjie::AST::TypeKind::TYPE_VARRAY) { |
| 1147 | auto ret = MakeOwned<ArrayLit>(); | 1148 | auto ret = MakeOwned<ArrayLit>(); |
| 1148 | - auto varrayTy = RawStaticCast<Cangjie::AST::VArrayTy *>(ty); | 1149 | + auto varrayTy = RawStaticCast<Cangjie::AST::VArrayTy *>(ty.Ty()); |
| 1149 | // Max size of varray: 65536 | 1150 | // Max size of varray: 65536 |
| 1150 | int64_t safe_size = (varrayTy->size > 0 && varrayTy->size <= 65536) ? varrayTy->size : 0; | 1151 | int64_t safe_size = (varrayTy->size > 0 && varrayTy->size <= 65536) ? varrayTy->size : 0; |
| 1151 | for (int64_t i = 0; i < safe_size; i++) { | 1152 | for (int64_t i = 0; i < safe_size; i++) { |
| @@ -1163,10 +1164,10 @@ OwnedPtr<Cangjie::AST::Expr> CangjieCompilerInstance::CreateInitializer(Ptr<Cang | |||
| 1163 | return nullptr; | 1164 | return nullptr; |
| 1164 | } | 1165 | } |
| 1165 | 1166 | ||
| 1166 | -Ptr<AST::Ty> CangjieCompilerInstance::AddObjectToFunctionTy(Ptr<AST::Ty> fTy, Ptr<AST::Ty> objectTy) | 1167 | +DataTy CangjieCompilerInstance::AddObjectToFunctionTy(DataTy fTy, ModalTy objectTy) |
| 1167 | { | 1168 | { |
| 1168 | - auto funcTy = RawStaticCast<FuncTy*>(fTy); | 1169 | + auto funcTy = StaticCast<FuncTy>(fTy); |
| 1169 | - std::vector<Ptr<Cangjie::AST::Ty>> ptys{objectTy}; | 1170 | + std::vector<ModalTy> ptys{objectTy}; |
| 1170 | for (auto &pty : funcTy->paramTys) { | 1171 | for (auto &pty : funcTy->paramTys) { |
| 1171 | ptys.emplace_back(pty); | 1172 | ptys.emplace_back(pty); |
| 1172 | } | 1173 | } |
| @@ -1200,8 +1201,8 @@ void CangjieCompilerInstance::AddCapturedvarsToCallExprOfLocalFunc() | |||
| 1200 | refExpr->ref.target = vd; | 1201 | refExpr->ref.target = vd; |
| 1201 | refExpr->SetTy(vd->GetTy()); | 1202 | refExpr->SetTy(vd->GetTy()); |
| 1202 | param->SetTy(vd->GetTy()); | 1203 | param->SetTy(vd->GetTy()); |
| 1203 | - fd->SetTy(AddObjectToFunctionTy(fd->GetTy(), vd->GetTy())); | 1204 | + fd->SetTy({AddObjectToFunctionTy(fd->DataTy(), vd->GetTy())}); |
| 1204 | - re->SetTy(AddObjectToFunctionTy(re->GetTy(), vd->GetTy())); | 1205 | + re->SetTy({AddObjectToFunctionTy(re->DataTy(), vd->GetTy()), re->TyMode()}); |
| 1205 | break; | 1206 | break; |
| 1206 | } | 1207 | } |
| 1207 | } | 1208 | } |
| @@ -43,14 +43,14 @@ private: | |||
| 43 | 43 | ||
| 44 | void AddImportNodeFromFakePkg(OwnedPtr<Cangjie::AST::File>& file, OwnedPtr<AST::Package>& fPkg); | 44 | void AddImportNodeFromFakePkg(OwnedPtr<Cangjie::AST::File>& file, OwnedPtr<AST::Package>& fPkg); |
| 45 | void AddImportSpecToLLDBExprPackage(); | 45 | void AddImportSpecToLLDBExprPackage(); |
| 46 | - Ptr<AST::Ty> AddObjectToFunctionTy(Ptr<AST::Ty> fTy, Ptr<AST::Ty> objectTy); | 46 | + DataTy AddObjectToFunctionTy(DataTy fTy, ModalTy objectTy); |
| 47 | void AddCapturedvarsToCallExprOfLocalFunc(); | 47 | void AddCapturedvarsToCallExprOfLocalFunc(); |
| 48 | std::vector<OwnedPtr<FuncArg>> CreateFuncArgsFromCapturedVars(std::string ident); | 48 | std::vector<OwnedPtr<FuncArg>> CreateFuncArgsFromCapturedVars(std::string ident); |
| 49 | void AddCapturedVarsToTryExpr(std::vector<OwnedPtr<Decl>>& localDecls, AST::TryExpr& tryExpr); | 49 | void AddCapturedVarsToTryExpr(std::vector<OwnedPtr<Decl>>& localDecls, AST::TryExpr& tryExpr); |
| 50 | void AddLocalsToExprResult(std::vector<OwnedPtr<Decl>>& localDecls); | 50 | void AddLocalsToExprResult(std::vector<OwnedPtr<Decl>>& localDecls); |
| 51 | void AddTyOfRefType(AST::RefType& rt); | 51 | void AddTyOfRefType(AST::RefType& rt); |
| 52 | void CheckTypeAndAddTy(OwnedPtr<AST::Type>& type); | 52 | void CheckTypeAndAddTy(OwnedPtr<AST::Type>& type); |
| 53 | - Ptr<AST::Ty> GetInstantiatedTy(AST::ClassTy& cTy, Ptr<AST::Ty> paramTy); | 53 | + DataTy GetInstantiatedTy(AST::ClassTy& cTy, DataTy paramTy); |
| 54 | OwnedPtr<AST::CallExpr> CreateSuperCall(AST::ClassDecl& cd); | 54 | OwnedPtr<AST::CallExpr> CreateSuperCall(AST::ClassDecl& cd); |
| 55 | void CreateInitFunc(AST::Decl& decl); | 55 | void CreateInitFunc(AST::Decl& decl); |
| 56 | void CreateDefaultCtor(AST::Decl& decl); | 56 | void CreateDefaultCtor(AST::Decl& decl); |
| @@ -60,14 +60,14 @@ private: | |||
| 60 | void CreateTyAndDefaultCtor(AST::Decl& decl, const std::vector<Ptr<AST::Ty>>& typeArgs); | 60 | void CreateTyAndDefaultCtor(AST::Decl& decl, const std::vector<Ptr<AST::Ty>>& typeArgs); |
| 61 | 61 | ||
| 62 | Ptr<AST::FuncDecl> GetInitFuncFromClassOrStruct( | 62 | Ptr<AST::FuncDecl> GetInitFuncFromClassOrStruct( |
| 63 | - std::vector<OwnedPtr<Decl>>& decls, std::vector<Ptr<Cangjie::AST::Ty>>& params); | 63 | + std::vector<OwnedPtr<Decl>>& decls, std::vector<ModalTy>& params); |
| 64 | - OwnedPtr<Cangjie::AST::Expr> CreatePrimitiveInitializer(Ptr<Cangjie::AST::Ty> ty); | 64 | + OwnedPtr<Cangjie::AST::Expr> CreatePrimitiveInitializer(ModalTy ty); |
| 65 | - OwnedPtr<Cangjie::AST::Expr> CreateFunctionInitializer(Ptr<Cangjie::AST::Ty> ty); | 65 | + OwnedPtr<Cangjie::AST::Expr> CreateFunctionInitializer(ModalTy ty); |
| 66 | - OwnedPtr<Cangjie::AST::Expr> CreateStructInitializer(Ptr<Cangjie::AST::Ty> ty); | 66 | + OwnedPtr<Cangjie::AST::Expr> CreateStructInitializer(ModalTy ty); |
| 67 | - OwnedPtr<Cangjie::AST::Expr> CreateClassInitializer(Ptr<Cangjie::AST::Ty> ty); | 67 | + OwnedPtr<Cangjie::AST::Expr> CreateClassInitializer(ModalTy ty); |
| 68 | - OwnedPtr<Cangjie::AST::Expr> CreateEnumInitializer(Ptr<Cangjie::AST::Ty> ty); | 68 | + OwnedPtr<Cangjie::AST::Expr> CreateEnumInitializer(ModalTy ty); |
| 69 | - OwnedPtr<Cangjie::AST::Expr> CreateRangeInitializer(Ptr<Cangjie::AST::Ty> ty); | 69 | + OwnedPtr<Cangjie::AST::Expr> CreateRangeInitializer(ModalTy ty); |
| 70 | - OwnedPtr<Cangjie::AST::Expr> CreateInitializer(Ptr<Cangjie::AST::Ty> ty); | 70 | + OwnedPtr<Cangjie::AST::Expr> CreateInitializer(ModalTy ty); |
| 71 | }; | 71 | }; |
| 72 | 72 | ||
| 73 | } // namespace CangjieExpr | 73 | } // namespace CangjieExpr |
| @@ -1904,7 +1904,7 @@ void CangjieDeclMap::AddFieldToRecordType(CompilerType& instancedType, Ptr<AST:: | |||
| 1904 | // Case generic type. | 1904 | // Case generic type. |
| 1905 | auto index = GetSubGenericTyIndex(decl, subTy->name); | 1905 | auto index = GetSubGenericTyIndex(decl, subTy->name); |
| 1906 | std::string subTypeName = instantiatedNames[index]; | 1906 | std::string subTypeName = instantiatedNames[index]; |
| 1907 | - subTy = ty->typeArgs[index]; | 1907 | + subTy = ty->TyArg(index); |
| 1908 | auto tempGenericType = this->GetGenericTypeByPartName(subTy->name); | 1908 | auto tempGenericType = this->GetGenericTypeByPartName(subTy->name); |
| 1909 | field_type = GetDynamicTypeFromTy(subTy, subTypeName, tempGenericType); | 1909 | field_type = GetDynamicTypeFromTy(subTy, subTypeName, tempGenericType); |
| 1910 | } else if (compilerTypeName.find(GENERIC_TYPE_PREFIX_NAME) != std::string::npos) { | 1910 | } else if (compilerTypeName.find(GENERIC_TYPE_PREFIX_NAME) != std::string::npos) { |
| @@ -1942,10 +1942,10 @@ Ptr<AST::Ty> CangjieDeclMap::GetMemberDeclTyByName(Ptr<AST::Ty> ty, std::string& | |||
| 1942 | continue; | 1942 | continue; |
| 1943 | } | 1943 | } |
| 1944 | if (memberDecls[i]->identifier.Val() == memberName) { | 1944 | if (memberDecls[i]->identifier.Val() == memberName) { |
| 1945 | - return memberDecls[i]->GetTy(); | 1945 | + return memberDecls[i]->DataTy(); |
| 1946 | } | 1946 | } |
| 1947 | } | 1947 | } |
| 1948 | - return decl->GetTy(); | 1948 | + return decl->DataTy(); |
| 1949 | } | 1949 | } |
| 1950 | 1950 | ||
| 1951 | CompilerType CangjieDeclMap::GetEnumType(Ptr<AST::Ty> ty, std::string enum_name, CompilerType& genericType) { | 1951 | CompilerType CangjieDeclMap::GetEnumType(Ptr<AST::Ty> ty, std::string enum_name, CompilerType& genericType) { |
| @@ -1994,14 +1994,14 @@ void CangjieDeclMap::CreateAndAddInheritTypeToRecordType(Ptr<AST::Ty> ty, Compil | |||
| 1994 | std::string member_name; | 1994 | std::string member_name; |
| 1995 | std::vector<std::string> instantiatedNames = builder.SplitCollectionName(enum_name); | 1995 | std::vector<std::string> instantiatedNames = builder.SplitCollectionName(enum_name); |
| 1996 | for (size_t j = 0; j < paramSize; j++) { | 1996 | for (size_t j = 0; j < paramSize; j++) { |
| 1997 | - auto subTy = ctorDecl->funcBody->paramLists[0]->params[j].get()->GetTy(); | 1997 | + auto subTy = ctorDecl->funcBody->paramLists[0]->params[j].get()->DataTy(); |
| 1998 | CompilerType ctor_para_type = ctorFuncType.GetFieldAtIndex(j + 1, member_name, nullptr, nullptr, nullptr); | 1998 | CompilerType ctor_para_type = ctorFuncType.GetFieldAtIndex(j + 1, member_name, nullptr, nullptr, nullptr); |
| 1999 | auto tempGenericType = ctor_para_type; | 1999 | auto tempGenericType = ctor_para_type; |
| 2000 | if (subTy->IsGeneric()) { | 2000 | if (subTy->IsGeneric()) { |
| 2001 | // case generic type. | 2001 | // case generic type. |
| 2002 | auto index = GetSubGenericTyIndex(decl, subTy->name); | 2002 | auto index = GetSubGenericTyIndex(decl, subTy->name); |
| 2003 | std::string subTypeName = instantiatedNames[index]; | 2003 | std::string subTypeName = instantiatedNames[index]; |
| 2004 | - subTy = ty->typeArgs[index]; | 2004 | + subTy = ty->TyArg(index); |
| 2005 | // Find generic type by name. | 2005 | // Find generic type by name. |
| 2006 | ctor_para_type = GetDynamicTypeFromTy(subTy, subTypeName, tempGenericType); | 2006 | ctor_para_type = GetDynamicTypeFromTy(subTy, subTypeName, tempGenericType); |
| 2007 | } | 2007 | } |
| @@ -2046,7 +2046,7 @@ CompilerType CangjieDeclMap::GetDynamicEnumType(Ptr<AST::Ty> ty, std::string typ | |||
| 2046 | if (enumKind == EnumLayout::E2OptionLike) { | 2046 | if (enumKind == EnumLayout::E2OptionLike) { |
| 2047 | ast->AddFieldToRecordType(dynamic_type, "constructor", enum_type, lldb::eAccessPublic, 0); | 2047 | ast->AddFieldToRecordType(dynamic_type, "constructor", enum_type, lldb::eAccessPublic, 0); |
| 2048 | auto args = builder.GetInstantiatedParamDeclName(typeName); | 2048 | auto args = builder.GetInstantiatedParamDeclName(typeName); |
| 2049 | - auto valType = GetDynamicTypeFromTy(ty->typeArgs[0], args[0], genericType); | 2049 | + auto valType = GetDynamicTypeFromTy(ty->TyArg(0), args[0], genericType); |
| 2050 | ast->AddFieldToRecordType(dynamic_type, "val", valType, lldb::eAccessPublic, 0); | 2050 | ast->AddFieldToRecordType(dynamic_type, "val", valType, lldb::eAccessPublic, 0); |
| 2051 | } else { | 2051 | } else { |
| 2052 | // add typeinfo*. | 2052 | // add typeinfo*. |
| @@ -2143,7 +2143,7 @@ CompilerType CangjieDeclMap::GetDynamicTupleType(Ptr<AST::Ty> ty, std::string ty | |||
| 2143 | std::vector<std::string> instantiatedNames = builder.SplitTupleName(typeName); | 2143 | std::vector<std::string> instantiatedNames = builder.SplitTupleName(typeName); |
| 2144 | CJC_ASSERT(instantiatedNames.size() == tupleTy->typeArgs.size()); | 2144 | CJC_ASSERT(instantiatedNames.size() == tupleTy->typeArgs.size()); |
| 2145 | for (size_t i = 0; i < tupleTy->typeArgs.size(); i++) { | 2145 | for (size_t i = 0; i < tupleTy->typeArgs.size(); i++) { |
| 2146 | - auto field_type = GetDynamicTypeFromTy(tupleTy->typeArgs[i], instantiatedNames[i], genericType); | 2146 | + auto field_type = GetDynamicTypeFromTy(tupleTy->TyArg(i), instantiatedNames[i], genericType); |
| 2147 | std::string field_name = "_" + std::to_string(i); | 2147 | std::string field_name = "_" + std::to_string(i); |
| 2148 | this->GetTypeSystem()->AddFieldToRecordType(instancedType, field_name.c_str(), field_type, lldb::eAccessPublic, 0); | 2148 | this->GetTypeSystem()->AddFieldToRecordType(instancedType, field_name.c_str(), field_type, lldb::eAccessPublic, 0); |
| 2149 | } | 2149 | } |
| @@ -2208,7 +2208,8 @@ CompilerType CangjieDeclMap::GetDynamicArrayType(Ptr<AST::Ty> ty, std::string ty | |||
| 2208 | // } | 2208 | // } |
| 2209 | // Add elements to array member. | 2209 | // Add elements to array member. |
| 2210 | std::vector<std::string> instantiatedNames = builder.SplitCollectionName(typeName); | 2210 | std::vector<std::string> instantiatedNames = builder.SplitCollectionName(typeName); |
| 2211 | - auto field_type = GetDynamicRawArrayType(arrayTy->typeArgs[0], instantiatedNames[0], genericType).GetPointerType(); | 2211 | + auto field_type = GetDynamicRawArrayType( |
| 2212 | + arrayTy->TyArg(0), instantiatedNames[0], genericType).GetPointerType(); | ||
| 2212 | this->GetTypeSystem()->AddFieldToRecordType(type, "rawptr", field_type, lldb::eAccessPublic, 0); | 2213 | this->GetTypeSystem()->AddFieldToRecordType(type, "rawptr", field_type, lldb::eAccessPublic, 0); |
| 2213 | 2214 | ||
| 2214 | CompilerType basic_type = this->GetTypeSystem()->GetBasicType(lldb::eBasicTypeLongLong).CreateTypedef( | 2215 | CompilerType basic_type = this->GetTypeSystem()->GetBasicType(lldb::eBasicTypeLongLong).CreateTypedef( |
| @@ -2247,7 +2248,8 @@ CompilerType CangjieDeclMap::CreateOptionReturnType(Ptr<Cangjie::AST::Ty> ty, st | |||
| 2247 | 2248 | ||
| 2248 | if (!valType.IsValid()) { | 2249 | if (!valType.IsValid()) { |
| 2249 | if (m_generic_types.find(ConstString(valTypeName[0])) != m_generic_types.end()) { | 2250 | if (m_generic_types.find(ConstString(valTypeName[0])) != m_generic_types.end()) { |
| 2250 | - valType = GetDynamicTypeFromTy(ty->typeArgs[0], valTypeName[0], m_generic_types[ConstString(valTypeName[0])]); | 2251 | + valType = GetDynamicTypeFromTy( |
| 2252 | + ty->TyArg(0), valTypeName[0], m_generic_types[ConstString(valTypeName[0])]); | ||
| 2251 | } | 2253 | } |
| 2252 | } | 2254 | } |
| 2253 | 2255 | ||
| @@ -210,7 +210,7 @@ bool CangjieExpressionParser::Parse(ExecutionContext &exeCtx, const std::string | |||
| 210 | m_result_type_name = instance->m_result_type_name; | 210 | m_result_type_name = instance->m_result_type_name; |
| 211 | std::string demangled_name = lldb_private::Mangled::GetDemangledTypeName(m_result_type_name); | 211 | std::string demangled_name = lldb_private::Mangled::GetDemangledTypeName(m_result_type_name); |
| 212 | if (!m_expr_result_type.IsValid() && demangled_name.find("std.core::Option<") == 0) { | 212 | if (!m_expr_result_type.IsValid() && demangled_name.find("std.core::Option<") == 0) { |
| 213 | - m_expr_result_type = m_expr_decl_map_up->CreateOptionReturnType(instance->m_expr_result->GetTy(), demangled_name); | 213 | + m_expr_result_type = m_expr_decl_map_up->CreateOptionReturnType(instance->m_expr_result->DataTy(), demangled_name); |
| 214 | return true; | 214 | return true; |
| 215 | } | 215 | } |
| 216 | if (!instance->m_expr_result->GetTy()->typeArgs.empty()) { | 216 | if (!instance->m_expr_result->GetTy()->typeArgs.empty()) { |
| @@ -223,7 +223,7 @@ bool CangjieExpressionParser::Parse(ExecutionContext &exeCtx, const std::string | |||
| 223 | m_expr_result_type = retType; | 223 | m_expr_result_type = retType; |
| 224 | } else { | 224 | } else { |
| 225 | m_expr_result_type = m_expr_decl_map_up->GetDynamicTypeFromGenericTypeInfo( | 225 | m_expr_result_type = m_expr_decl_map_up->GetDynamicTypeFromGenericTypeInfo( |
| 226 | - instance->m_expr_result->GetTy(), demangled_name); | 226 | + instance->m_expr_result->DataTy(), demangled_name); |
| 227 | } | 227 | } |
| 228 | } | 228 | } |
| 229 | return true; | 229 | return true; |
| @@ -905,7 +905,8 @@ namespace llvm { | |||
| 905 | bool isCJMutexLock() const { return equals("CJ_MCC_MutexLock"); } | 905 | bool isCJMutexLock() const { return equals("CJ_MCC_MutexLock"); } |
| 906 | 906 | ||
| 907 | bool isCangjieNewObjFunction() const { | 907 | bool isCangjieNewObjFunction() const { |
| 908 | - return equals("CJ_MCC_NewObject") || equals("CJ_MCC_NewFinalizer"); | 908 | + return equals("CJ_MCC_NewObject") || equals("CJ_MCC_NewLocalObject") || |
| 909 | + equals("CJ_MCC_NewFinalizer"); | ||
| 909 | } | 910 | } |
| 910 | 911 | ||
| 911 | bool isCangjieTypeInfo() const { return startswith("TypeInfo"); } | 912 | bool isCangjieTypeInfo() const { return startswith("TypeInfo"); } |
| @@ -66,6 +66,10 @@ struct AssignGeneric { | |||
| 66 | enum { DstPtr, SrcPtr, TypeInfo }; | 66 | enum { DstPtr, SrcPtr, TypeInfo }; |
| 67 | }; | 67 | }; |
| 68 | 68 | ||
| 69 | +struct AssignLocalGeneric { | ||
| 70 | + enum { DstPtr, SrcPtr, TypeInfo }; | ||
| 71 | +}; | ||
| 72 | + | ||
| 69 | struct ArrayCopy { | 73 | struct ArrayCopy { |
| 70 | enum { DstObj, DstPtr, SrcObj, SrcPtr, Size }; | 74 | enum { DstObj, DstPtr, SrcObj, SrcPtr, Size }; |
| 71 | }; | 75 | }; |
| @@ -1237,11 +1237,11 @@ public: | |||
| 1237 | /// of its movement if necessary. | 1237 | /// of its movement if necessary. |
| 1238 | /// @{ | 1238 | /// @{ |
| 1239 | void replaceElements(DINodeArray Elements) { | 1239 | void replaceElements(DINodeArray Elements) { |
| 1240 | -#ifndef NDEBUG | 1240 | +// #ifndef NDEBUG |
| 1241 | - for (DINode *Op : getElements()) | 1241 | +// for (DINode *Op : getElements()) |
| 1242 | - assert(is_contained(Elements->operands(), Op) && | 1242 | +// assert(is_contained(Elements->operands(), Op) && |
| 1243 | - "Lost a member during member list replacement"); | 1243 | +// "Lost a member during member list replacement"); |
| 1244 | -#endif | 1244 | +// #endif |
| 1245 | replaceOperandWith(4, Elements.get()); | 1245 | replaceOperandWith(4, Elements.get()); |
| 1246 | } | 1246 | } |
| 1247 | 1247 | ||
| @@ -472,6 +472,18 @@ def int_cj_array_copy_struct : Intrinsic<[], | |||
| 472 | [llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptr_ty, llvm_anyint_ty]>; | 472 | [llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptr_ty, llvm_anyint_ty]>; |
| 473 | def int_cj_copy_struct_field : Intrinsic<[], | 473 | def int_cj_copy_struct_field : Intrinsic<[], |
| 474 | [llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptr_ty, llvm_anyint_ty]>; | 474 | [llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptr_ty, llvm_anyint_ty]>; |
| 475 | +def int_cj_maybe_local_write_ref : Intrinsic<[], | ||
| 476 | + [llvm_gcptr_ty, llvm_gcptrgcptr_ty, llvm_gcptr_ty], | ||
| 477 | + [IntrArgMemOnly, NoCapture<ArgIndex<0>>, NoCapture<ArgIndex<1>>]>; | ||
| 478 | +def int_cj_maybe_local_write_generic : Intrinsic<[], | ||
| 479 | + [llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptr_ty, llvm_i32_ty], | ||
| 480 | + [IntrArgMemOnly, IntrWillReturn, | ||
| 481 | + NoCapture<ArgIndex<0>>, | ||
| 482 | + NoCapture<ArgIndex<1>>, WriteOnly<ArgIndex<1>>, | ||
| 483 | + NoCapture<ArgIndex<2>>, NoAlias<ArgIndex<2>>, ReadOnly<ArgIndex<2>>]>; | ||
| 484 | +def int_cj_demode_write_ref : Intrinsic<[], | ||
| 485 | + [llvm_gcptr_ty, llvm_gcptrgcptr_ty, llvm_gcptr_ty], | ||
| 486 | + [IntrArgMemOnly, NoCapture<ArgIndex<0>>, NoCapture<ArgIndex<1>>]>; | ||
| 475 | def int_cj_cross_access_barrier : Intrinsic<[], [llvm_i64_ty]>; | 487 | def int_cj_cross_access_barrier : Intrinsic<[], [llvm_i64_ty]>; |
| 476 | def int_cj_get_exported_ref : Intrinsic<[llvm_gcptr_ty], | 488 | def int_cj_get_exported_ref : Intrinsic<[llvm_gcptr_ty], |
| 477 | [llvm_i64_ty]>; | 489 | [llvm_i64_ty]>; |
| @@ -629,6 +641,12 @@ def int_cj_assign_generic : Intrinsic<[], | |||
| 629 | NoCapture<ArgIndex<0>>, NoAlias<ArgIndex<0>>, | 641 | NoCapture<ArgIndex<0>>, NoAlias<ArgIndex<0>>, |
| 630 | NoCapture<ArgIndex<1>>, NoAlias<ArgIndex<1>>, ReadOnly<ArgIndex<1>>, | 642 | NoCapture<ArgIndex<1>>, NoAlias<ArgIndex<1>>, ReadOnly<ArgIndex<1>>, |
| 631 | NoCapture<ArgIndex<2>>, NoAlias<ArgIndex<2>>, ReadOnly<ArgIndex<2>>]>; | 643 | NoCapture<ArgIndex<2>>, NoAlias<ArgIndex<2>>, ReadOnly<ArgIndex<2>>]>; |
| 644 | +def int_cj_assign_local_generic : Intrinsic<[], | ||
| 645 | + [llvm_gcptr_ty, llvm_gcptr_ty, llvm_ptr_ty], | ||
| 646 | + [IntrArgMemOnly, IntrWillReturn, | ||
| 647 | + NoCapture<ArgIndex<0>>, NoAlias<ArgIndex<0>>, | ||
| 648 | + NoCapture<ArgIndex<1>>, NoAlias<ArgIndex<1>>, ReadOnly<ArgIndex<1>>, | ||
| 649 | + NoCapture<ArgIndex<2>>, NoAlias<ArgIndex<2>>, ReadOnly<ArgIndex<2>>]>; | ||
| 632 | def int_cj_malloc_array_generic : Intrinsic<[llvm_gcptr_ty], | 650 | def int_cj_malloc_array_generic : Intrinsic<[llvm_gcptr_ty], |
| 633 | [llvm_ptr_ty, llvm_i64_ty], | 651 | [llvm_ptr_ty, llvm_i64_ty], |
| 634 | [Throws]>; | 652 | [Throws]>; |
| @@ -636,6 +654,18 @@ def int_cj_array_copy_generic : Intrinsic<[], | |||
| 636 | [llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptr_ty, llvm_anyint_ty]>; | 654 | [llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptr_ty, llvm_anyint_ty]>; |
| 637 | def int_cj_gcwrite_generic_payload : Intrinsic<[], | 655 | def int_cj_gcwrite_generic_payload : Intrinsic<[], |
| 638 | [llvm_gcptr_ty, llvm_ptr_ty, llvm_i32_ty]>; | 656 | [llvm_gcptr_ty, llvm_ptr_ty, llvm_i32_ty]>; |
| 657 | +def int_cj_malloc_local_object : Intrinsic<[llvm_gcptr_ty], | ||
| 658 | + [llvm_ptr_ty, llvm_i32_ty], | ||
| 659 | + [Throws]>; | ||
| 660 | +def int_cj_malloc_local_array : Intrinsic<[llvm_gcptr_ty], | ||
| 661 | + [llvm_ptr_ty, llvm_i64_ty, llvm_i64_ty], | ||
| 662 | + [Throws]>; | ||
| 663 | +def int_cj_malloc_local_array_generic : Intrinsic<[llvm_gcptr_ty], | ||
| 664 | + [llvm_ptr_ty, llvm_i64_ty], | ||
| 665 | + [Throws]>; | ||
| 666 | +def int_cj_alloca_local_generic : Intrinsic<[llvm_gcptr_ty], | ||
| 667 | + [llvm_ptr_ty, llvm_i32_ty], | ||
| 668 | + [Throws]>; | ||
| 639 | 669 | ||
| 640 | //===------------------- ObjC ARC runtime Intrinsics --------------------===// | 670 | //===------------------- ObjC ARC runtime Intrinsics --------------------===// |
| 641 | // | 671 | // |
| @@ -94,6 +94,7 @@ void initializeCJBarrierOptLegacyPassPass(PassRegistry &); | |||
| 94 | void initializeCJBarrierSplitLegacyPassPass(PassRegistry &); | 94 | void initializeCJBarrierSplitLegacyPassPass(PassRegistry &); |
| 95 | void initializeCJRuntimeLoweringLegacyPassPass(PassRegistry &); | 95 | void initializeCJRuntimeLoweringLegacyPassPass(PassRegistry &); |
| 96 | void initializeCJGenericIntrinsicOptLegacyPassPass(PassRegistry &); | 96 | void initializeCJGenericIntrinsicOptLegacyPassPass(PassRegistry &); |
| 97 | +void initializeCJInsertRemoveLocalFinalizerLegacyPassPass(PassRegistry &); | ||
| 97 | void initializeCJIRVerifierLegacyPassPass(PassRegistry &); | 98 | void initializeCJIRVerifierLegacyPassPass(PassRegistry &); |
| 98 | void initializeCJLoopFloatOptLegacyPassPass(PassRegistry &); | 99 | void initializeCJLoopFloatOptLegacyPassPass(PassRegistry &); |
| 99 | void initializeCFGOnlyPrinterLegacyPassPass(PassRegistry&); | 100 | void initializeCFGOnlyPrinterLegacyPassPass(PassRegistry&); |
| @@ -613,6 +613,13 @@ ModulePass *createCJBarrierSplitLegacyPass(); | |||
| 613 | // | 613 | // |
| 614 | FunctionPass *createCJGenericIntrinsicOptLegacyPass(); | 614 | FunctionPass *createCJGenericIntrinsicOptLegacyPass(); |
| 615 | 615 | ||
| 616 | +//===----------------------------------------------------------------------===// | ||
| 617 | +// | ||
| 618 | +// CJInsertRemoveLocalFinalizer - Pair every CJ_MCC_AddLocalFinalizer with a | ||
| 619 | +// CJ_MCC_RemoveLocalFinalizer at the end of the object's live range. | ||
| 620 | +// | ||
| 621 | +FunctionPass *createCJInsertRemoveLocalFinalizerLegacyPass(); | ||
| 622 | + | ||
| 616 | //===----------------------------------------------------------------------===// | 623 | //===----------------------------------------------------------------------===// |
| 617 | // | 624 | // |
| 618 | // InsertCJTBAA - insert Cangjie TBAA Metadata | 625 | // InsertCJTBAA - insert Cangjie TBAA Metadata |
| @@ -130,7 +130,7 @@ struct AllocaAnalysisData { | |||
| 130 | } | 130 | } |
| 131 | }; | 131 | }; |
| 132 | 132 | ||
| 133 | -enum MemoryAccess { | 133 | +enum MemoryAccessKind { |
| 134 | MemoryNone = 0, | 134 | MemoryNone = 0, |
| 135 | DefineMemory = 0b01, | 135 | DefineMemory = 0b01, |
| 136 | UseMemory = 0b10, | 136 | UseMemory = 0b10, |
| @@ -281,7 +281,7 @@ public: | |||
| 281 | void visitMemorySelect(SelectInst *SI, SetVector<FieldInfo *> &AllocaDefs, | 281 | void visitMemorySelect(SelectInst *SI, SetVector<FieldInfo *> &AllocaDefs, |
| 282 | SetVector<FieldInfo *> &AllocaUses); | 282 | SetVector<FieldInfo *> &AllocaUses); |
| 283 | 283 | ||
| 284 | - MemoryAccess hasMemoryDefineOrUseValue(Instruction *I, | 284 | + MemoryAccessKind hasMemoryDefineOrUseValue(Instruction *I, |
| 285 | SetVector<FieldInfo *> &AllocaDefs, | 285 | SetVector<FieldInfo *> &AllocaDefs, |
| 286 | SetVector<FieldInfo *> &AllocaUses); | 286 | SetVector<FieldInfo *> &AllocaUses); |
| 287 | 287 | ||
| @@ -0,0 +1,43 @@ | |||
| 1 | +//===- CJInsertRemoveLocalFinalizer.h - -------------------------*- C++ -*-===// | ||
| 2 | +// | ||
| 3 | +// Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved. | ||
| 4 | +// This source file is part of the Cangjie project, licensed under Apache-2.0 | ||
| 5 | +// with Runtime Library Exception. | ||
| 6 | +// | ||
| 7 | +// See https://cangjie-lang.cn/pages/LICENSE for license information. | ||
| 8 | +// | ||
| 9 | +//===----------------------------------------------------------------------===// | ||
| 10 | +// | ||
| 11 | +// This file provides interface to the "Cangjie Insert Remove Local Finalizer" | ||
| 12 | +// pass. | ||
| 13 | +// | ||
| 14 | +// CJRuntimeLowering registers every LocalMode finalizer object with | ||
| 15 | +// CJ_MCC_AddLocalFinalizer. This pass pairs each such registration with a | ||
| 16 | +// CJ_MCC_RemoveLocalFinalizer call at the end of the object's live range, on | ||
| 17 | +// every control-flow path, exactly once. | ||
| 18 | +// | ||
| 19 | +// It must run before CJRewriteStatepoint: the Remove call is a potential | ||
| 20 | +// safepoint, so the statepoint rewriter has to see it in order to relocate the | ||
| 21 | +// object pointer it takes. | ||
| 22 | +// | ||
| 23 | +//===----------------------------------------------------------------------===// | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | +namespace llvm { | ||
| 31 | + | ||
| 32 | +class Function; | ||
| 33 | + | ||
| 34 | +class CJInsertRemoveLocalFinalizer | ||
| 35 | + : public PassInfoMixin<CJInsertRemoveLocalFinalizer> { | ||
| 36 | +public: | ||
| 37 | + CJInsertRemoveLocalFinalizer() = default; | ||
| 38 | + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); | ||
| 39 | +}; | ||
| 40 | + | ||
| 41 | +} // end namespace llvm | ||
| 42 | + | ||
| 43 | + | ||
| @@ -192,6 +192,7 @@ getLocInfoForCJIntrinsics(const IntrinsicInst *II, | |||
| 192 | Base = MemoryLocation::getAfter(getBaseObj(II)); | 192 | Base = MemoryLocation::getAfter(getBaseObj(II)); |
| 193 | break; | 193 | break; |
| 194 | case Intrinsic::cj_assign_generic: | 194 | case Intrinsic::cj_assign_generic: |
| 195 | + case Intrinsic::cj_assign_local_generic: | ||
| 195 | Src = MemoryLocation::getAfter(getSource(II)); | 196 | Src = MemoryLocation::getAfter(getSource(II)); |
| 196 | Dst = MemoryLocation::getAfter(getDest(II)); | 197 | Dst = MemoryLocation::getAfter(getDest(II)); |
| 197 | break; | 198 | break; |
| @@ -258,7 +259,8 @@ ModRefInfo CJAAResult::getModRefInfo(const CallBase *Call, | |||
| 258 | case Intrinsic::cj_gcwrite_struct: | 259 | case Intrinsic::cj_gcwrite_struct: |
| 259 | case Intrinsic::cj_gcwrite_generic: | 260 | case Intrinsic::cj_gcwrite_generic: |
| 260 | case Intrinsic::cj_gcread_generic: | 261 | case Intrinsic::cj_gcread_generic: |
| 261 | - case Intrinsic::cj_assign_generic: { | 262 | + case Intrinsic::cj_assign_generic: |
| 263 | + case Intrinsic::cj_assign_local_generic: { | ||
| 262 | auto [LocS, LocD, LocB] = | 264 | auto [LocS, LocD, LocB] = |
| 263 | getLocInfoForCJIntrinsics(cast<IntrinsicInst>(Call), TLI); | 265 | getLocInfoForCJIntrinsics(cast<IntrinsicInst>(Call), TLI); |
| 264 | if (!LocS || !LocD) | 266 | if (!LocS || !LocD) |
| @@ -80,6 +80,7 @@ const static StdMap<unsigned, StringRef> IntrinsicMap{ | |||
| 80 | {Intrinsic::cj_atomic_swap, "CJ_MCC_AtomicSwapReference"}, | 80 | {Intrinsic::cj_atomic_swap, "CJ_MCC_AtomicSwapReference"}, |
| 81 | {Intrinsic::cj_atomic_compare_swap, "CJ_MCC_AtomicCompareAndSwapReference"}, | 81 | {Intrinsic::cj_atomic_compare_swap, "CJ_MCC_AtomicCompareAndSwapReference"}, |
| 82 | {Intrinsic::cj_assign_generic, "CJ_MCC_AssignGeneric"}, | 82 | {Intrinsic::cj_assign_generic, "CJ_MCC_AssignGeneric"}, |
| 83 | + {Intrinsic::cj_assign_local_generic, "CJ_MCC_AssignLocalGeneric"}, | ||
| 83 | {Intrinsic::cj_gcwrite_generic, "CJ_MCC_WriteGeneric"}, | 84 | {Intrinsic::cj_gcwrite_generic, "CJ_MCC_WriteGeneric"}, |
| 84 | {Intrinsic::cj_gcread_generic, "CJ_MCC_ReadGeneric"}, | 85 | {Intrinsic::cj_gcread_generic, "CJ_MCC_ReadGeneric"}, |
| 85 | {Intrinsic::cj_array_copy_generic, "CJ_MCC_ArrayCopyGeneric"}, | 86 | {Intrinsic::cj_array_copy_generic, "CJ_MCC_ArrayCopyGeneric"}, |
| @@ -1074,6 +1075,7 @@ bool CJBarrierLowering::isCJBarrier(Instruction *I) { | |||
| 1074 | case Intrinsic::cj_array_copy_struct: | 1075 | case Intrinsic::cj_array_copy_struct: |
| 1075 | case Intrinsic::cj_array_copy_generic: | 1076 | case Intrinsic::cj_array_copy_generic: |
| 1076 | case Intrinsic::cj_assign_generic: | 1077 | case Intrinsic::cj_assign_generic: |
| 1078 | + case Intrinsic::cj_assign_local_generic: | ||
| 1077 | case Intrinsic::cj_gcwrite_generic: | 1079 | case Intrinsic::cj_gcwrite_generic: |
| 1078 | case Intrinsic::cj_gcread_generic: | 1080 | case Intrinsic::cj_gcread_generic: |
| 1079 | case Intrinsic::cj_gcwrite_generic_payload: | 1081 | case Intrinsic::cj_gcwrite_generic_payload: |
| @@ -565,8 +565,8 @@ public: | |||
| 565 | insertLiveSet(LiveTmp, FI, Imm); | 565 | insertLiveSet(LiveTmp, FI, Imm); |
| 566 | continue; | 566 | continue; |
| 567 | } | 567 | } |
| 568 | - assert(MFI.isStatepointSpillSlotObjectIndex(FI) && | 568 | + // assert(MFI.isStatepointSpillSlotObjectIndex(FI) && |
| 569 | - "Unknown stack type"); | 569 | + // "Unknown stack type"); |
这里为什么需要注释掉 ![]() ![]() | |||
| 570 | continue; | 570 | continue; |
| 571 | } | 571 | } |
| 572 | 572 | ||
| @@ -1130,6 +1130,9 @@ bool TargetPassConfig::addISelPasses() { | |||
| 1130 | addPass(createCJFillMetadataLegacyPass()); | 1130 | addPass(createCJFillMetadataLegacyPass()); |
| 1131 | addPass(createCJRuntimeLoweringLegacyPass()); | 1131 | addPass(createCJRuntimeLoweringLegacyPass()); |
| 1132 | addPass(createCangjieSpecificOptLegacyPass(0)); | 1132 | addPass(createCangjieSpecificOptLegacyPass(0)); |
| 1133 | + // Must precede CJRewriteStatepoint: the Remove call is a potential | ||
| 1134 | + // safepoint, so the object pointer it takes has to be relocated. | ||
| 1135 | + addPass(createCJInsertRemoveLocalFinalizerLegacyPass()); | ||
| 1133 | addPass(createPlaceSafepointsLegacyPass()); | 1136 | addPass(createPlaceSafepointsLegacyPass()); |
| 1134 | addPass(createCJRewriteStatepointLegacyPass(0)); | 1137 | addPass(createCJRewriteStatepointLegacyPass(0)); |
| 1135 | } | 1138 | } |
| @@ -85,6 +85,7 @@ Value *getDest(const CallBase *CI) { | |||
| 85 | case Intrinsic::cj_gcread_generic: | 85 | case Intrinsic::cj_gcread_generic: |
| 86 | return CI->getArgOperand(GCReadGeneric::DstPtr); | 86 | return CI->getArgOperand(GCReadGeneric::DstPtr); |
| 87 | case Intrinsic::cj_assign_generic: | 87 | case Intrinsic::cj_assign_generic: |
| 88 | + case Intrinsic::cj_assign_local_generic: | ||
| 88 | return CI->getArgOperand(AssignGeneric::DstPtr); | 89 | return CI->getArgOperand(AssignGeneric::DstPtr); |
| 89 | case Intrinsic::cj_array_copy_ref: | 90 | case Intrinsic::cj_array_copy_ref: |
| 90 | case Intrinsic::cj_array_copy_struct: | 91 | case Intrinsic::cj_array_copy_struct: |
| @@ -111,6 +112,7 @@ Value *getSource(const CallBase *CI) { | |||
| 111 | case Intrinsic::cj_gcread_generic: | 112 | case Intrinsic::cj_gcread_generic: |
| 112 | return CI->getArgOperand(GCReadGeneric::SrcPtr); | 113 | return CI->getArgOperand(GCReadGeneric::SrcPtr); |
| 113 | case Intrinsic::cj_assign_generic: | 114 | case Intrinsic::cj_assign_generic: |
| 115 | + case Intrinsic::cj_assign_local_generic: | ||
| 114 | return CI->getArgOperand(AssignGeneric::SrcPtr); | 116 | return CI->getArgOperand(AssignGeneric::SrcPtr); |
| 115 | case Intrinsic::cj_array_copy_ref: | 117 | case Intrinsic::cj_array_copy_ref: |
| 116 | case Intrinsic::cj_array_copy_struct: | 118 | case Intrinsic::cj_array_copy_struct: |
| @@ -163,6 +163,7 @@ | |||
| 163 | 163 | ||
| 164 | 164 | ||
| 165 | 165 | ||
| 166 | + | ||
| 166 | 167 | ||
| 167 | 168 | ||
| 168 | 169 | ||
| @@ -1260,6 +1261,10 @@ Error PassBuilder::parseModulePass(ModulePassManager &MPM, | |||
| 1260 | } | 1261 | } |
| 1261 | if (CJPipeline && !CangjieLTOPreOpt) { | 1262 | if (CJPipeline && !CangjieLTOPreOpt) { |
| 1262 | MPM.addPass(CJSpecificOpt(L.getSpeedupLevel())); | 1263 | MPM.addPass(CJSpecificOpt(L.getSpeedupLevel())); |
| 1264 | + // Must precede CJRewriteStatepoint: the Remove call is a potential | ||
| 1265 | + // safepoint, so the object pointer it takes has to be relocated. | ||
| 1266 | + MPM.addPass( | ||
| 1267 | + createModuleToFunctionPassAdaptor(CJInsertRemoveLocalFinalizer())); | ||
| 1263 | MPM.addPass(PlaceSafepoints()); | 1268 | MPM.addPass(PlaceSafepoints()); |
| 1264 | MPM.addPass(CJBarrierOpt()); | 1269 | MPM.addPass(CJBarrierOpt()); |
| 1265 | MPM.addPass(CJRewriteStatepoint(L.getSpeedupLevel())); | 1270 | MPM.addPass(CJRewriteStatepoint(L.getSpeedupLevel())); |
| @@ -403,6 +403,7 @@ FUNCTION_PASS("transform-warning", WarnMissedTransformationsPass()) | |||
| 403 | FUNCTION_PASS("tsan", ThreadSanitizerPass()) | 403 | FUNCTION_PASS("tsan", ThreadSanitizerPass()) |
| 404 | FUNCTION_PASS("memprof", MemProfilerPass()) | 404 | FUNCTION_PASS("memprof", MemProfilerPass()) |
| 405 | FUNCTION_PASS("cj-generic-intrinsic-opt", CJGenericIntrinsicOpt()) | 405 | FUNCTION_PASS("cj-generic-intrinsic-opt", CJGenericIntrinsicOpt()) |
| 406 | +FUNCTION_PASS("cj-insert-remove-local-finalizer", CJInsertRemoveLocalFinalizer()) | ||
| 406 | FUNCTION_PASS("insert-cj-tbaa", InsertCJTBAA()) | 407 | FUNCTION_PASS("insert-cj-tbaa", InsertCJTBAA()) |
| 407 | FUNCTION_PASS("cj-gcinstr-replace", CJGCInstrReplace()) | 408 | FUNCTION_PASS("cj-gcinstr-replace", CJGCInstrReplace()) |
| 408 | FUNCTION_PASS("cj-gcinstr-restore", CJGCInstrRestore()) | 409 | FUNCTION_PASS("cj-gcinstr-restore", CJGCInstrRestore()) |
| @@ -1593,6 +1593,7 @@ private: | |||
| 1593 | return true; | 1593 | return true; |
| 1594 | case Intrinsic::cj_gcwrite_static_struct: | 1594 | case Intrinsic::cj_gcwrite_static_struct: |
| 1595 | case Intrinsic::cj_assign_generic: | 1595 | case Intrinsic::cj_assign_generic: |
| 1596 | + case Intrinsic::cj_assign_local_generic: | ||
| 1596 | case Intrinsic::cj_copy_struct_field: | 1597 | case Intrinsic::cj_copy_struct_field: |
| 1597 | handleGCWriteAgg(II, true); | 1598 | handleGCWriteAgg(II, true); |
| 1598 | return true; | 1599 | return true; |
| @@ -624,6 +624,9 @@ void PassManagerBuilder::populateModulePassManager( | |||
| 624 | auto addCangjiePasses = [&]() { | 624 | auto addCangjiePasses = [&]() { |
| 625 | if (CJPipeline) { | 625 | if (CJPipeline) { |
| 626 | MPM.add(createCangjieSpecificOptLegacyPass(OptLevel)); | 626 | MPM.add(createCangjieSpecificOptLegacyPass(OptLevel)); |
| 627 | + // Must precede CJRewriteStatepoint: the Remove call is a potential | ||
| 628 | + // safepoint, so the object pointer it takes has to be relocated. | ||
| 629 | + MPM.add(createCJInsertRemoveLocalFinalizerLegacyPass()); | ||
| 627 | MPM.add(createPlaceSafepointsLegacyPass()); | 630 | MPM.add(createPlaceSafepointsLegacyPass()); |
| 628 | MPM.add(createCJRewriteStatepointLegacyPass(OptLevel)); | 631 | MPM.add(createCJRewriteStatepointLegacyPass(OptLevel)); |
| 629 | } | 632 | } |
| @@ -2817,6 +2817,7 @@ static bool isAllocSiteRemovable(Instruction *AI, | |||
| 2817 | Users.emplace_back(I); | 2817 | Users.emplace_back(I); |
| 2818 | continue; | 2818 | continue; |
| 2819 | case Intrinsic::cj_assign_generic: | 2819 | case Intrinsic::cj_assign_generic: |
| 2820 | + case Intrinsic::cj_assign_local_generic: | ||
| 2820 | if (II->getArgOperand(1) == PI) | 2821 | if (II->getArgOperand(1) == PI) |
| 2821 | return false; | 2822 | return false; |
| 2822 | Users.emplace_back(I); | 2823 | Users.emplace_back(I); |
| @@ -784,7 +784,7 @@ void StructLiveAnalysis::computeFieldKillSet(BasicBlock *BB, | |||
| 784 | SetVector<FieldInfo *> MemDefSet; | 784 | SetVector<FieldInfo *> MemDefSet; |
| 785 | SetVector<FieldInfo *> MemUseSet; | 785 | SetVector<FieldInfo *> MemUseSet; |
| 786 | 786 | ||
| 787 | - MemoryAccess Status = hasMemoryDefineOrUseValue(&I, MemDefSet, MemUseSet); | 787 | + MemoryAccessKind Status = hasMemoryDefineOrUseValue(&I, MemDefSet, MemUseSet); |
| 788 | if (Status & DefineMemory) { | 788 | if (Status & DefineMemory) { |
| 789 | KillSet.set_union(MemDefSet); | 789 | KillSet.set_union(MemDefSet); |
| 790 | } | 790 | } |
| @@ -1260,7 +1260,7 @@ void StructLiveAnalysis::visitMemorySelect(SelectInst *SI, | |||
| 1260 | 1260 | ||
| 1261 | // If it is an instruction that contains the memory parameter, then handles | 1261 | // If it is an instruction that contains the memory parameter, then handles |
| 1262 | // use and define values according to the instruction type. | 1262 | // use and define values according to the instruction type. |
| 1263 | -MemoryAccess StructLiveAnalysis::hasMemoryDefineOrUseValue( | 1263 | +MemoryAccessKind StructLiveAnalysis::hasMemoryDefineOrUseValue( |
| 1264 | Instruction *I, SetVector<FieldInfo *> &AllocaDefs, | 1264 | Instruction *I, SetVector<FieldInfo *> &AllocaDefs, |
| 1265 | SetVector<FieldInfo *> &AllocaUses) { | 1265 | SetVector<FieldInfo *> &AllocaUses) { |
| 1266 | // For each memory instruction, identifies and processes use and define, | 1266 | // For each memory instruction, identifies and processes use and define, |
| @@ -1291,7 +1291,7 @@ MemoryAccess StructLiveAnalysis::hasMemoryDefineOrUseValue( | |||
| 1291 | Status |= static_cast<int>(DefineMemory); | 1291 | Status |= static_cast<int>(DefineMemory); |
| 1292 | if (!AllocaUses.empty()) | 1292 | if (!AllocaUses.empty()) |
| 1293 | Status |= static_cast<int>(UseMemory); | 1293 | Status |= static_cast<int>(UseMemory); |
| 1294 | - return static_cast<MemoryAccess>(Status); | 1294 | + return static_cast<MemoryAccessKind>(Status); |
| 1295 | } | 1295 | } |
| 1296 | 1296 | ||
| 1297 | void StructLiveAnalysis::combineStructGCField(StructLiveSetTy &FieldInfos, | 1297 | void StructLiveAnalysis::combineStructGCField(StructLiveSetTy &FieldInfos, |
| @@ -285,6 +285,7 @@ public: | |||
| 285 | break; | 285 | break; |
| 286 | case Intrinsic::cj_gc_statepoint: | 286 | case Intrinsic::cj_gc_statepoint: |
| 287 | case Intrinsic::cj_malloc_object: | 287 | case Intrinsic::cj_malloc_object: |
| 288 | + case Intrinsic::cj_malloc_local_object: | ||
| 288 | case Intrinsic::cj_malloc_array: | 289 | case Intrinsic::cj_malloc_array: |
| 289 | case Intrinsic::cj_malloc_array_generic: | 290 | case Intrinsic::cj_malloc_array_generic: |
| 290 | case Intrinsic::cj_division_check_sdiv: | 291 | case Intrinsic::cj_division_check_sdiv: |
| @@ -293,6 +294,7 @@ public: | |||
| 293 | case Intrinsic::cj_division_check_urem: | 294 | case Intrinsic::cj_division_check_urem: |
| 294 | case Intrinsic::cj_throw_exception: | 295 | case Intrinsic::cj_throw_exception: |
| 295 | case Intrinsic::cj_alloca_generic: | 296 | case Intrinsic::cj_alloca_generic: |
| 297 | + case Intrinsic::cj_alloca_local_generic: | ||
| 296 | break; | 298 | break; |
| 297 | } | 299 | } |
| 298 | } | 300 | } |
| @@ -0,0 +1,512 @@ | |||
| 1 | +//===- CJInsertRemoveLocalFinalizer.cpp - ---------------------------------===// | ||
| 2 | +// | ||
| 3 | +// Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved. | ||
| 4 | +// This source file is part of the Cangjie project, licensed under Apache-2.0 | ||
| 5 | +// with Runtime Library Exception. | ||
| 6 | +// | ||
| 7 | +// See https://cangjie-lang.cn/pages/LICENSE for license information. | ||
| 8 | +// | ||
| 9 | +//===----------------------------------------------------------------------===// | ||
| 10 | +// | ||
| 11 | +// Pair every CJ_MCC_AddLocalFinalizer with a CJ_MCC_RemoveLocalFinalizer at the | ||
| 12 | +// end of the object's live range, on every control-flow path, exactly once. | ||
| 13 | +// | ||
| 14 | +// "Right after the last use" is not that point: a path that never uses the | ||
| 15 | +// object (the other arm of a branch, a landing pad, a loop exit) has no last | ||
| 16 | +// use at all, and a use reached from several paths would be unregistered | ||
| 17 | +// several times. | ||
| 18 | +// | ||
| 19 | +// The point we want is the dead-out frontier of the object's live range. | ||
| 20 | +// Liveness is closed under predecessors -- if a value is live at p it is live | ||
| 21 | +// at every point from which p is reachable -- so the live range is left exactly | ||
| 22 | +// once on every path, and inserting at the frontier gives exactly one Remove | ||
| 23 | +// per path: | ||
| 24 | +// | ||
| 25 | +// * block-internal death: no representative is live out of B | ||
| 26 | +// -> insert after the last point in B that defines or uses one; | ||
| 27 | +// * edge death: some representative is live out of B, but none is live on the | ||
| 28 | +// edge B -> S | ||
| 29 | +// -> insert on that edge, splitting it when it is critical. | ||
| 30 | +// | ||
| 31 | +// A "representative" is any SSA value that keeps the object alive: the object | ||
| 32 | +// itself, everything derived from it by bitcast / addrspacecast / GEP / select | ||
| 33 | +// / phi, and -- to bridge the store->load round trip through a local variable | ||
| 34 | +// -- the address of every stack slot it is stored into plus every value loaded | ||
| 35 | +// from such a slot. Including the slot address is what keeps the live range | ||
| 36 | +// connected across memory: without it the object would look dead right after | ||
| 37 | +// the store. | ||
| 38 | +// | ||
| 39 | +// If the object reaches memory we cannot see through (a heap field, a slot | ||
| 40 | +// whose address escapes), no Remove is emitted for it at all. Leaking a | ||
| 41 | +// registration is far less harmful than unregistering an object that is still | ||
| 42 | +// reachable. | ||
| 43 | +// | ||
| 44 | +// The pass keys off the CJ_MCC_AddLocalFinalizer calls rather than off the | ||
| 45 | +// allocation: after CJRuntimeLowering both heap and local finalizer objects are | ||
| 46 | +// produced by CJ_MCC_NewFinalizer, so the Add call is the only thing that still | ||
| 47 | +// tells the two apart. | ||
| 48 | +// | ||
| 49 | +//===----------------------------------------------------------------------===// | ||
| 50 | + | ||
| 51 | + | ||
| 52 | + | ||
| 53 | + | ||
| 54 | + | ||
| 55 | + | ||
| 56 | + | ||
| 57 | + | ||
| 58 | + | ||
| 59 | + | ||
| 60 | + | ||
| 61 | + | ||
| 62 | + | ||
| 63 | + | ||
| 64 | + | ||
| 65 | + | ||
| 66 | + | ||
| 67 | + | ||
| 68 | + | ||
| 69 | + | ||
| 70 | + | ||
| 71 | + | ||
| 72 | + | ||
| 73 | + | ||
| 74 | + | ||
| 75 | +using namespace llvm; | ||
| 76 | + | ||
| 77 | +namespace { | ||
| 78 | + | ||
| 79 | +const char AddLocalFinalizerName[] = "CJ_MCC_AddLocalFinalizer"; | ||
| 80 | +const char RemoveLocalFinalizerName[] = "CJ_MCC_RemoveLocalFinalizer"; | ||
| 81 | + | ||
| 82 | +using RepSet = SmallPtrSet<Value *, 8>; | ||
| 83 | +using BlockSets = DenseMap<const BasicBlock *, RepSet>; | ||
| 84 | +using Edge = std::pair<BasicBlock *, BasicBlock *>; | ||
| 85 | + | ||
| 86 | +// Walk the address chain of \p AI. Returns false if the slot is used in any way | ||
| 87 | +// other than loading/storing through it, which would let its contents flow | ||
| 88 | +// somewhere we cannot follow. On success \p Addrs receives the alloca and every | ||
| 89 | +// cast/GEP of it, so that touching the slot counts as keeping the object alive. | ||
| 90 | +bool collectSlotAddresses(AllocaInst *AI, SmallPtrSetImpl<Value *> &Addrs) { | ||
| 91 | + SmallVector<Value *, 8> Worklist{AI}; | ||
| 92 | + Addrs.insert(AI); | ||
| 93 | + while (!Worklist.empty()) { | ||
| 94 | + Value *V = Worklist.pop_back_val(); | ||
| 95 | + for (User *U : V->users()) { | ||
| 96 | + if (auto *LI = dyn_cast<LoadInst>(U)) { | ||
| 97 | + if (LI->getPointerOperand() != V) | ||
| 98 | + return false; | ||
| 99 | + } else if (auto *SI = dyn_cast<StoreInst>(U)) { | ||
| 100 | + // Storing through the slot is fine; storing the slot's own address | ||
| 101 | + // somewhere else is not. | ||
| 102 | + if (SI->getPointerOperand() != V) | ||
| 103 | + return false; | ||
| 104 | + } else if (isa<BitCastInst>(U) || isa<AddrSpaceCastInst>(U) || | ||
| 105 | + isa<GetElementPtrInst>(U)) { | ||
| 106 | + if (Addrs.insert(U).second) | ||
| 107 | + Worklist.push_back(U); | ||
| 108 | + } else if (auto *II = dyn_cast<IntrinsicInst>(U)) { | ||
| 109 | + Intrinsic::ID ID = II->getIntrinsicID(); | ||
| 110 | + if (ID != Intrinsic::lifetime_start && ID != Intrinsic::lifetime_end && | ||
| 111 | + !isa<DbgInfoIntrinsic>(II)) | ||
| 112 | + return false; | ||
| 113 | + } else { | ||
| 114 | + return false; | ||
| 115 | + } | ||
| 116 | + } | ||
| 117 | + } | ||
| 118 | + return true; | ||
| 119 | +} | ||
| 120 | + | ||
| 121 | +// Collect every value that keeps \p Obj alive into \p Reps. Returns false if the | ||
| 122 | +// object escapes into memory that cannot be tracked, in which case no Remove may | ||
| 123 | +// be emitted for it. | ||
| 124 | +bool collectRepresentatives(Value *Obj, ArrayRef<LoadInst *> AllLoads, | ||
| 125 | + RepSet &Reps) { | ||
| 126 | + SmallVector<Value *, 8> Worklist; | ||
| 127 | + SmallPtrSet<AllocaInst *, 4> Slots; | ||
| 128 | + auto AddRep = [&](Value *V) { | ||
| 129 | + if (Reps.insert(V).second) | ||
| 130 | + Worklist.push_back(V); | ||
| 131 | + }; | ||
| 132 | + AddRep(Obj); | ||
| 133 | + while (!Worklist.empty()) { | ||
| 134 | + Value *V = Worklist.pop_back_val(); | ||
| 135 | + for (User *U : V->users()) { | ||
| 136 | + if (auto *SI = dyn_cast<StoreInst>(U)) { | ||
| 137 | + if (SI->getValueOperand() != V) | ||
| 138 | + continue; // V is only the destination, that is a plain use | ||
| 139 | + auto *AI = | ||
| 140 | + dyn_cast<AllocaInst>(getUnderlyingObject(SI->getPointerOperand())); | ||
| 141 | + if (AI == nullptr) | ||
| 142 | + return false; // stored into memory we do not own | ||
| 143 | + if (!Slots.insert(AI).second) | ||
| 144 | + continue; | ||
| 145 | + SmallPtrSet<Value *, 8> Addrs; | ||
| 146 | + if (!collectSlotAddresses(AI, Addrs)) | ||
| 147 | + return false; // the slot's contents can escape | ||
| 148 | + for (Value *Addr : Addrs) | ||
| 149 | + Reps.insert(Addr); | ||
| 150 | + // Anything ever loaded from this slot may be the object. | ||
| 151 | + for (LoadInst *LI : AllLoads) | ||
| 152 | + if (Addrs.count(LI->getPointerOperand())) | ||
| 153 | + AddRep(LI); | ||
| 154 | + continue; | ||
| 155 | + } | ||
| 156 | + if (isa<BitCastInst>(U) || isa<AddrSpaceCastInst>(U) || | ||
| 157 | + isa<GetElementPtrInst>(U) || isa<SelectInst>(U) || isa<PHINode>(U)) | ||
| 158 | + AddRep(U); | ||
| 159 | + } | ||
| 160 | + } | ||
| 161 | + return true; | ||
| 162 | +} | ||
| 163 | + | ||
| 164 | +// Backward liveness dataflow restricted to \p Reps. A value used by a phi is | ||
| 165 | +// live on the incoming edge rather than in the phi's own block, so those uses | ||
| 166 | +// seed the predecessor's live-out set instead of the block's use set. | ||
| 167 | +// | ||
| 168 | +// A representative phi additionally seeds *every* incoming edge, not just the | ||
| 169 | +// ones whose incoming value is itself a representative. Such a phi defines a | ||
| 170 | +// representative in the middle of the live range, which would otherwise be a | ||
| 171 | +// second entry into it: a path entering through the phi's other operand would | ||
| 172 | +// leave the range twice and unregister the object twice. Extending the range | ||
| 173 | +// back over all incoming edges keeps it single-entry, so it is still left | ||
| 174 | +// exactly once on every path. | ||
| 175 | +void computeRepLiveness(Function &F, const RepSet &Reps, BlockSets &Defs, | ||
| 176 | + BlockSets &LiveIn, BlockSets &LiveOut) { | ||
| 177 | + BlockSets UpExposed; | ||
| 178 | + for (BasicBlock &BB : F) { | ||
| 179 | + RepSet &Def = Defs[&BB]; | ||
| 180 | + RepSet &Up = UpExposed[&BB]; | ||
| 181 | + for (Instruction &I : reverse(BB)) { | ||
| 182 | + if (Reps.count(&I)) { | ||
| 183 | + Def.insert(&I); | ||
| 184 | + Up.erase(&I); | ||
| 185 | + } | ||
| 186 | + if (isa<PHINode>(I)) | ||
| 187 | + continue; | ||
| 188 | + for (Value *Op : I.operands()) | ||
| 189 | + if (Reps.count(Op)) | ||
| 190 | + Up.insert(Op); | ||
| 191 | + } | ||
| 192 | + RepSet &Out = LiveOut[&BB]; | ||
| 193 | + for (BasicBlock *Succ : successors(&BB)) | ||
| 194 | + for (PHINode &PN : Succ->phis()) { | ||
| 195 | + if (Reps.count(&PN)) | ||
| 196 | + Out.insert(&PN); | ||
把 x = phi[ xx, bbxx, yy bby] 放到当前bb的liveout , 这一步是有什么深意么 ![]() ![]() | |||
| 197 | + Value *V = PN.getIncomingValueForBlock(&BB); | ||
| 198 | + if (Reps.count(V)) | ||
| 199 | + Out.insert(V); | ||
| 200 | + } | ||
| 201 | + LiveIn[&BB] = Up; | ||
| 202 | + } | ||
| 203 | + | ||
| 204 | + SmallSetVector<BasicBlock *, 32> Worklist; | ||
| 205 | + for (BasicBlock &BB : F) | ||
| 206 | + Worklist.insert(&BB); | ||
| 207 | + while (!Worklist.empty()) { | ||
| 208 | + BasicBlock *BB = Worklist.pop_back_val(); | ||
| 209 | + RepSet &Out = LiveOut[BB]; | ||
| 210 | + for (BasicBlock *Succ : successors(BB)) | ||
| 211 | + Out.insert(LiveIn[Succ].begin(), LiveIn[Succ].end()); | ||
| 212 | + RepSet &In = LiveIn[BB]; | ||
| 213 | + const size_t OldSize = In.size(); | ||
| 214 | + for (Value *V : Out) | ||
| 215 | + if (!Defs[BB].count(V)) | ||
| 216 | + In.insert(V); | ||
| 217 | + if (In.size() != OldSize) | ||
| 218 | + for (BasicBlock *Pred : predecessors(BB)) | ||
| 219 | + Worklist.insert(Pred); | ||
| 220 | + } | ||
| 221 | +} | ||
| 222 | + | ||
| 223 | +class LocalFinalizerRemover { | ||
| 224 | +public: | ||
| 225 | + LocalFinalizerRemover(Function &F, DominatorTree &DT) | ||
| 226 | + : F(F), DT(DT), Bdr(F.getContext()) {} | ||
| 227 | + | ||
| 228 | + bool run(); | ||
| 229 | + | ||
| 230 | +private: | ||
| 231 | + void process(Instruction *Obj); | ||
| 232 | + void emitRemove(Value *Obj, const DebugLoc &DL); | ||
| 233 | + void emitRemoveAfter(Value *Obj, Instruction *After); | ||
| 234 | + void emitRemoveOnEdges(Value *Obj, ArrayRef<Edge> DeadEdges); | ||
| 235 | + | ||
| 236 | + Function &F; | ||
| 237 | + DominatorTree &DT; | ||
| 238 | + IRBuilder<> Bdr; | ||
| 239 | + Function *RemoveFunc = nullptr; | ||
| 240 | + SmallVector<LoadInst *, 32> AllLoads; | ||
| 241 | +}; | ||
| 242 | + | ||
| 243 | +void LocalFinalizerRemover::emitRemove(Value *Obj, const DebugLoc &DL) { | ||
| 244 | + Value *Arg = Bdr.CreatePointerBitCastOrAddrSpaceCast( | ||
| 245 | + Obj, RemoveFunc->getFunctionType()->getParamType(0)); | ||
| 246 | + Bdr.CreateCall(RemoveFunc, {Arg})->setDebugLoc(DL); | ||
| 247 | +} | ||
| 248 | + | ||
| 249 | +// Insert the Remove call directly after \p After. A run of phi nodes must stay | ||
| 250 | +// contiguous at the top of its block, so a phi is not inserted after but | ||
| 251 | +// skipped past. | ||
| 252 | +void LocalFinalizerRemover::emitRemoveAfter(Value *Obj, Instruction *After) { | ||
| 253 | + BasicBlock *BB = After->getParent(); | ||
| 254 | + BasicBlock::iterator IP = isa<PHINode>(After) | ||
| 255 | + ? BB->getFirstInsertionPt() | ||
| 256 | + : std::next(After->getIterator()); | ||
| 257 | + Bdr.SetInsertPoint(BB, IP); | ||
| 258 | + emitRemove(Obj, After->getDebugLoc()); | ||
| 259 | +} | ||
| 260 | + | ||
| 261 | +// Insert one Remove call on each of \p DeadEdges. Placing the call on the edge | ||
| 262 | +// rather than at the top of the successor is what keeps a shared successor (a | ||
| 263 | +// landing pad reached from several invokes, a merge block) from getting one | ||
| 264 | +// call per incoming edge, and what keeps the object's definition dominating the | ||
| 265 | +// call. | ||
| 266 | +void LocalFinalizerRemover::emitRemoveOnEdges(Value *Obj, | ||
| 267 | + ArrayRef<Edge> DeadEdges) { | ||
| 268 | + const DebugLoc DL = cast<Instruction>(Obj)->getDebugLoc(); | ||
| 269 | + | ||
| 270 | + MapVector<BasicBlock *, SmallSetVector<BasicBlock *, 2>> BySucc; | ||
| 271 | + for (const Edge &E : DeadEdges) | ||
| 272 | + BySucc[E.second].insert(E.first); | ||
| 273 | + | ||
| 274 | + for (auto &KV : BySucc) { | ||
| 275 | + BasicBlock *Succ = KV.first; | ||
| 276 | + // When every edge into Succ is dead the call belongs in Succ itself: no | ||
| 277 | + // path reaches Succ with the object still live, and no edge needs a split. | ||
| 278 | + // This is the common shape for a landing pad shared by invokes that all | ||
| 279 | + // consume the object. | ||
| 280 | + SmallPtrSet<BasicBlock *, 4> AllPreds(pred_begin(Succ), pred_end(Succ)); | ||
| 281 | + if (KV.second.size() == AllPreds.size()) { | ||
| 282 | + Bdr.SetInsertPoint(Succ, Succ->getFirstInsertionPt()); | ||
| 283 | + emitRemove(Obj, DL); | ||
| 284 | + continue; | ||
| 285 | + } | ||
| 286 | + for (BasicBlock *Pred : KV.second) { | ||
| 287 | + Instruction *TI = Pred->getTerminator(); | ||
| 288 | + if (TI->getNumSuccessors() == 1) { | ||
| 289 | + // Not a critical edge, and a single-successor terminator cannot be | ||
| 290 | + // consuming the object itself. | ||
| 291 | + Bdr.SetInsertPoint(TI); | ||
| 292 | + emitRemove(Obj, DL); | ||
| 293 | + continue; | ||
| 294 | + } | ||
| 295 | + BasicBlock *NewBB = nullptr; | ||
| 296 | + if (Succ->isEHPad()) { | ||
| 297 | + // SplitEdge/ehAwareSplitEdge build funclet pads, which do not fit the | ||
| 298 | + // landing-pad EH the Cangjie runtime uses. | ||
| 299 | + if (!isa<LandingPadInst>(Succ->getFirstNonPHI())) { | ||
| 300 | + LLVM_DEBUG(dbgs() << DEBUG_TYPE ": cannot split edge into " | ||
| 301 | + << Succ->getName() << ", no " | ||
| 302 | + << RemoveLocalFinalizerName << " there\n"); | ||
| 303 | + continue; | ||
| 304 | + } | ||
| 305 | + SmallVector<BasicBlock *, 2> NewBBs; | ||
| 306 | + SplitLandingPadPredecessors(Succ, {Pred}, ".fin", ".fin.lp", NewBBs, | ||
| 307 | + &DT); | ||
| 308 | + NewBB = NewBBs[0]; | ||
| 309 | + } else { | ||
| 310 | + NewBB = SplitEdge(Pred, Succ, &DT); | ||
| 311 | + } | ||
| 312 | + if (NewBB == nullptr) | ||
| 313 | + continue; | ||
| 314 | + Bdr.SetInsertPoint(NewBB->getTerminator()); | ||
| 315 | + emitRemove(Obj, DL); | ||
| 316 | + } | ||
| 317 | + } | ||
| 318 | +} | ||
| 319 | + | ||
| 320 | +void LocalFinalizerRemover::process(Instruction *ObjDef) { | ||
| 321 | + Value *Obj = ObjDef; | ||
| 322 | + RepSet Reps; | ||
| 323 | + if (!collectRepresentatives(Obj, AllLoads, Reps)) { | ||
| 324 | + LLVM_DEBUG(dbgs() << DEBUG_TYPE ": local finalizer escapes, no " | ||
| 325 | + << RemoveLocalFinalizerName << " emitted for " << *Obj | ||
| 326 | + << "\n"); | ||
| 327 | + return; | ||
| 328 | + } | ||
| 329 | + | ||
| 330 | + BlockSets Defs, LiveIn, LiveOut; | ||
| 331 | + computeRepLiveness(F, Reps, Defs, LiveIn, LiveOut); | ||
| 332 | + | ||
| 333 | + // Is any representative live on the edge BB -> Succ? A phi operand is live on | ||
| 334 | + // its incoming edge even though the phi is defined in Succ, so it has to be | ||
| 335 | + // checked separately from Succ's live-in set. | ||
| 336 | + auto liveOnEdge = [&](BasicBlock *BB, BasicBlock *Succ) { | ||
| 337 | + if (!LiveIn[Succ].empty()) | ||
| 338 | + return true; | ||
| 339 | + // Mirrors the live-out seeding in computeRepLiveness. | ||
| 340 | + for (PHINode &PN : Succ->phis()) | ||
| 341 | + if (Reps.count(&PN) || Reps.count(PN.getIncomingValueForBlock(BB))) | ||
| 342 | + return true; | ||
| 343 | + return false; | ||
| 344 | + }; | ||
| 345 | + | ||
| 346 | + // In valid SSA liveness implies the definition dominates, with one exception: | ||
| 347 | + // on the unwind edge of the invoke that produces the object there is no | ||
| 348 | + // object, and CJ_MCC_AddLocalFinalizer was never reached either. | ||
| 349 | + auto defAvailableOnEdge = [&](BasicBlock *BB, BasicBlock *Succ) { | ||
| 350 | + if (auto *II = dyn_cast<InvokeInst>(ObjDef)) | ||
| 351 | + if (II->getParent() == BB && Succ == II->getUnwindDest()) | ||
| 352 | + return false; | ||
| 353 | + return DT.dominates(ObjDef->getParent(), BB); | ||
| 354 | + }; | ||
| 355 | + | ||
| 356 | + SmallVector<Instruction *, 4> AfterPoints; | ||
| 357 | + SmallVector<Edge, 4> DeadEdges; | ||
| 358 | + | ||
| 359 | + for (BasicBlock &BB : F) { | ||
| 360 | + if (LiveOut[&BB].empty()) { | ||
| 361 | + // Nothing survives BB: the object dies here, if it lived here at all. | ||
| 362 | + Instruction *Last = nullptr; | ||
| 363 | + for (Instruction &I : BB) { | ||
| 364 | + if (Reps.count(&I)) { | ||
| 365 | + Last = &I; | ||
| 366 | + continue; | ||
| 367 | + } | ||
| 368 | + if (isa<PHINode>(I)) | ||
| 369 | + continue; // a phi's operands are used on the incoming edge | ||
| 370 | + for (Value *Op : I.operands()) | ||
| 371 | + if (Reps.count(Op)) { | ||
| 372 | + Last = &I; | ||
| 373 | + break; | ||
| 374 | + } | ||
| 375 | + } | ||
| 376 | + if (Last == nullptr) | ||
| 377 | + continue; // no representative activity in this block | ||
| 378 | + if (!Last->isTerminator()) { | ||
| 379 | + // A representative loaded from a reused slot can appear on paths the | ||
| 380 | + // object itself never reached; require the definition to be available. | ||
| 381 | + if (Last != ObjDef && !DT.dominates(Obj, Last)) | ||
| 382 | + continue; | ||
| 383 | + AfterPoints.push_back(Last); | ||
| 384 | + continue; | ||
| 385 | + } | ||
| 386 | + // The terminator itself consumes the object (it is passed to an invoke): | ||
| 387 | + // it is dead on every outgoing edge. A terminator without successors | ||
| 388 | + // (`ret obj`) hands the object to the caller, so there is nothing to | ||
| 389 | + // unregister here. | ||
| 390 | + for (BasicBlock *Succ : successors(&BB)) | ||
| 391 | + if (defAvailableOnEdge(&BB, Succ)) | ||
| 392 | + DeadEdges.emplace_back(&BB, Succ); | ||
| 393 | + continue; | ||
| 394 | + } | ||
| 395 | + for (BasicBlock *Succ : successors(&BB)) | ||
| 396 | + if (!liveOnEdge(&BB, Succ) && defAvailableOnEdge(&BB, Succ)) | ||
| 397 | + DeadEdges.emplace_back(&BB, Succ); | ||
| 398 | + } | ||
| 399 | + | ||
| 400 | + for (Instruction *At : AfterPoints) | ||
| 401 | + emitRemoveAfter(Obj, At); | ||
| 402 | + if (!DeadEdges.empty()) | ||
| 403 | + emitRemoveOnEdges(Obj, DeadEdges); | ||
| 404 | +} | ||
| 405 | + | ||
| 406 | +bool LocalFinalizerRemover::run() { | ||
| 407 | + Module &M = *F.getParent(); | ||
| 408 | + Function *AddFunc = M.getFunction(AddLocalFinalizerName); | ||
| 409 | + if (AddFunc == nullptr || AddFunc->use_empty()) | ||
| 410 | + return false; | ||
| 411 | + | ||
| 412 | + // Collect the registered objects in program order, and every load in one | ||
| 413 | + // sweep. Inserting Remove calls adds no loads, so AllLoads stays complete. | ||
| 414 | + SmallVector<Instruction *, 8> Objs; | ||
| 415 | + SmallPtrSet<Value *, 8> Seen; | ||
| 416 | + for (Instruction &I : instructions(F)) { | ||
| 417 | + if (auto *LI = dyn_cast<LoadInst>(&I)) { | ||
| 418 | + AllLoads.push_back(LI); | ||
| 419 | + continue; | ||
| 420 | + } | ||
| 421 | + auto *CB = dyn_cast<CallBase>(&I); | ||
| 422 | + if (CB == nullptr || CB->getCalledFunction() != AddFunc || | ||
| 423 | + CB->arg_size() < 1) | ||
| 424 | + continue; | ||
| 425 | + auto *Obj = dyn_cast<Instruction>(CB->getArgOperand(0)); | ||
| 426 | + if (Obj == nullptr) { | ||
| 427 | + LLVM_DEBUG(dbgs() << DEBUG_TYPE ": registered value is not an " | ||
| 428 | + "instruction, skipping " | ||
| 429 | + << *CB << "\n"); | ||
| 430 | + continue; | ||
| 431 | + } | ||
| 432 | + if (Seen.insert(Obj).second) | ||
| 433 | + Objs.push_back(Obj); | ||
| 434 | + } | ||
| 435 | + if (Objs.empty()) | ||
| 436 | + return false; | ||
| 437 | + | ||
| 438 | + // Already paired up, e.g. because the pass ran twice over this function. | ||
| 439 | + Function *Existing = M.getFunction(RemoveLocalFinalizerName); | ||
| 440 | + if (Existing != nullptr) { | ||
| 441 | + llvm::erase_if(Objs, [&](Instruction *Obj) { | ||
| 442 | + return llvm::any_of(Obj->users(), [&](User *U) { | ||
| 443 | + auto *CB = dyn_cast<CallBase>(U); | ||
| 444 | + return CB != nullptr && CB->getCalledFunction() == Existing; | ||
| 445 | + }); | ||
| 446 | + }); | ||
| 447 | + if (Objs.empty()) | ||
| 448 | + return false; | ||
| 449 | + } | ||
| 450 | + | ||
| 451 | + RemoveFunc = Existing; | ||
| 452 | + if (RemoveFunc == nullptr) { | ||
| 453 | + IRBuilder<> Decl(F.getContext()); | ||
| 454 | + FunctionType *FT = | ||
| 455 | + FunctionType::get(Decl.getVoidTy(), {Decl.getInt8PtrTy(1)}, false); | ||
| 456 | + RemoveFunc = M.declareCJRuntimeFunc(RemoveLocalFinalizerName, FT, false); | ||
| 457 | + RemoveFunc->addFnAttr(Attribute::get(F.getContext(), "gc-leaf-function")); | ||
| 458 | + } | ||
| 459 | + | ||
| 460 | + for (Instruction *Obj : Objs) | ||
| 461 | + process(Obj); | ||
| 462 | + return true; | ||
| 463 | +} | ||
| 464 | + | ||
| 465 | +class CJInsertRemoveLocalFinalizerLegacyPass : public FunctionPass { | ||
| 466 | +public: | ||
| 467 | + static char ID; | ||
| 468 | + | ||
| 469 | + explicit CJInsertRemoveLocalFinalizerLegacyPass() : FunctionPass(ID) { | ||
| 470 | + initializeCJInsertRemoveLocalFinalizerLegacyPassPass( | ||
| 471 | + *PassRegistry::getPassRegistry()); | ||
| 472 | + } | ||
| 473 | + ~CJInsertRemoveLocalFinalizerLegacyPass() = default; | ||
| 474 | + | ||
| 475 | + bool runOnFunction(Function &F) override { | ||
| 476 | + if (F.isDeclaration()) | ||
| 477 | + return false; | ||
| 478 | + auto &DT = getAnalysis<DominatorTreeWrapperPass>().getDomTree(); | ||
| 479 | + return LocalFinalizerRemover(F, DT).run(); | ||
| 480 | + } | ||
| 481 | + | ||
| 482 | + void getAnalysisUsage(AnalysisUsage &AU) const override { | ||
| 483 | + AU.addRequired<DominatorTreeWrapperPass>(); | ||
| 484 | + } | ||
| 485 | +}; | ||
| 486 | + | ||
| 487 | +} // namespace | ||
| 488 | + | ||
| 489 | +PreservedAnalyses | ||
| 490 | +CJInsertRemoveLocalFinalizer::run(Function &F, FunctionAnalysisManager &AM) { | ||
| 491 | + if (F.isDeclaration()) | ||
| 492 | + return PreservedAnalyses::all(); | ||
| 493 | + auto &DT = AM.getResult<DominatorTreeAnalysis>(F); | ||
| 494 | + if (!LocalFinalizerRemover(F, DT).run()) | ||
| 495 | + return PreservedAnalyses::all(); | ||
| 496 | + // Splitting critical edges changes the CFG. | ||
| 497 | + return PreservedAnalyses::none(); | ||
| 498 | +} | ||
| 499 | + | ||
| 500 | +char CJInsertRemoveLocalFinalizerLegacyPass::ID = 0; | ||
| 501 | + | ||
| 502 | +FunctionPass *llvm::createCJInsertRemoveLocalFinalizerLegacyPass() { | ||
| 503 | + return new CJInsertRemoveLocalFinalizerLegacyPass(); | ||
| 504 | +} | ||
| 505 | + | ||
| 506 | +INITIALIZE_PASS_BEGIN(CJInsertRemoveLocalFinalizerLegacyPass, | ||
| 507 | + "cj-insert-remove-local-finalizer", | ||
| 508 | + "Cangjie Insert Remove Local Finalizer", false, false) | ||
| 509 | +INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) | ||
| 510 | +INITIALIZE_PASS_END(CJInsertRemoveLocalFinalizerLegacyPass, | ||
| 511 | + "cj-insert-remove-local-finalizer", | ||
| 512 | + "Cangjie Insert Remove Local Finalizer", false, false) | ||
| @@ -106,7 +106,16 @@ const static StdMap<unsigned, StringRef> RuntimeMap { | |||
| 106 | {Intrinsic::cj_remove_exported_ref, "CJ_MCC_RemoveExportedRef"}, | 106 | {Intrinsic::cj_remove_exported_ref, "CJ_MCC_RemoveExportedRef"}, |
| 107 | {Intrinsic::cj_create_export_handle, "CJ_MCC_CreateExportHandle"}, | 107 | {Intrinsic::cj_create_export_handle, "CJ_MCC_CreateExportHandle"}, |
| 108 | {Intrinsic::cj_blackhole, "CJ_LLVM_BlackHole"}, | 108 | {Intrinsic::cj_blackhole, "CJ_LLVM_BlackHole"}, |
| 109 | - {Intrinsic::cj_get_lambda_addr, "CJ_MCC_GetJSLambdaAddr"}}; | 109 | + {Intrinsic::cj_get_lambda_addr, "CJ_MCC_GetJSLambdaAddr"}, |
| 110 | + {Intrinsic::cj_malloc_local_object, "CJ_MCC_NewLocalObject"}, | ||
| 111 | + {Intrinsic::cj_alloca_local_generic, "CJ_MCC_NewLocalObject"}, | ||
| 112 | + {Intrinsic::cj_malloc_local_array, "CJ_MCC_NewLocalArray"}, | ||
| 113 | + {Intrinsic::cj_malloc_local_array_generic, "CJ_MCC_NewLocalGenericArray"}, | ||
| 114 | + {Intrinsic::cj_maybe_local_write_ref, "CJ_MCC_MaybeLocalWriteRef"}, | ||
| 115 | + {Intrinsic::cj_maybe_local_write_generic, | ||
| 116 | + "CJ_MCC_MaybeLocalWriteGeneric"}, | ||
| 117 | + {Intrinsic::cj_demode_write_ref, "CJ_MCC_DemodeWriteRef"}, | ||
| 118 | + }; | ||
| 110 | 119 | ||
| 111 | struct LowerGetFieldOffset { | 120 | struct LowerGetFieldOffset { |
| 112 | CallBase *CI; | 121 | CallBase *CI; |
| @@ -389,6 +398,31 @@ public: | |||
| 389 | CI->setCalledFunction(Func); | 398 | CI->setCalledFunction(Func); |
| 390 | } | 399 | } |
| 391 | 400 | ||
| 401 | + void replaceNewLocalFinalizerFunc(CallBase *CI) { | ||
| 402 | + replaceWithRuntimeFunc(CI, false, true); | ||
| 403 | + | ||
| 404 | + IRBuilder<> Bdr(CI->getContext()); | ||
| 405 | + // CJ_MCC_NewFinalizer returns the newly created finalizer object; pass it | ||
| 406 | + // to CJ_MCC_AddLocalFinalizer to register it. | ||
| 407 | + Function *AddFunc = M.getFunction("CJ_MCC_AddLocalFinalizer"); | ||
| 408 | + if (AddFunc == nullptr) { | ||
| 409 | + FunctionType *FT = FunctionType::get(Bdr.getVoidTy(), {Bdr.getInt8PtrTy(1)}, false); | ||
| 410 | + AddFunc = M.declareCJRuntimeFunc("CJ_MCC_AddLocalFinalizer", FT, false); | ||
| 411 | + AddFunc->addFnAttr(Attribute::get(CI->getContext(), "gc-leaf-function")); | ||
| 412 | + } | ||
| 413 | + if (auto *II = dyn_cast<InvokeInst>(CI)) { | ||
| 414 | + // For an InvokeInst the result is only valid on the normal path, so | ||
| 415 | + // insert the call at the start of the normal destination. | ||
| 416 | + BasicBlock *NormalDest = II->getNormalDest(); | ||
| 417 | + Bdr.SetInsertPoint(NormalDest, NormalDest->getFirstInsertionPt()); | ||
| 418 | + } else { | ||
| 419 | + // Insert the Add call right after the NewFinalizer call. | ||
| 420 | + Bdr.SetInsertPoint(CI->getParent(), std::next(CI->getIterator())); | ||
| 421 | + } | ||
| 422 | + auto *AddCall = Bdr.CreateCall(AddFunc, {CI}); | ||
| 423 | + AddCall->setDebugLoc(CI->getDebugLoc()); | ||
| 424 | + } | ||
| 425 | + | ||
| 392 | void replaceNewWeakRefFunc(CallBase *CI) { | 426 | void replaceNewWeakRefFunc(CallBase *CI) { |
| 393 | IRBuilder<> Bdr(CI); | 427 | IRBuilder<> Bdr(CI); |
| 394 | Function *Func = M.getFunction("CJ_MCC_NewWeakRefObject"); | 428 | Function *Func = M.getFunction("CJ_MCC_NewWeakRefObject"); |
| @@ -399,6 +433,16 @@ public: | |||
| 399 | CI->setCalledFunction(Func); | 433 | CI->setCalledFunction(Func); |
| 400 | } | 434 | } |
| 401 | 435 | ||
| 436 | + void replaceNewLocalWeakRefFunc(CallBase *CI) { | ||
| 437 | + IRBuilder<> Bdr(CI); | ||
| 438 | + Function *Func = M.getFunction("CJ_MCC_NewLocalWeakRefObject"); | ||
| 439 | + if (Func == nullptr) { | ||
| 440 | + Func = M.declareCJRuntimeFunc("CJ_MCC_NewLocalWeakRefObject", | ||
| 441 | + CI->getFunctionType(), false); | ||
| 442 | + } | ||
| 443 | + CI->setCalledFunction(Func); | ||
| 444 | + } | ||
| 445 | + | ||
| 402 | // llvm.cj.alloca.generic(TypeInfo* ti, i64 size) | 446 | // llvm.cj.alloca.generic(TypeInfo* ti, i64 size) |
| 403 | void replaceAllocaGeneric(CallBase *CI, EscapeScope &ES) { | 447 | void replaceAllocaGeneric(CallBase *CI, EscapeScope &ES) { |
| 404 | if (replaceWithAlloca(CI, ES)) { | 448 | if (replaceWithAlloca(CI, ES)) { |
| @@ -409,6 +453,13 @@ public: | |||
| 409 | setMetadata(CI, "new_gen"); | 453 | setMetadata(CI, "new_gen"); |
| 410 | } | 454 | } |
| 411 | 455 | ||
| 456 | + // llvm.cj.alloca.generic(TypeInfo* ti, i64 size) | ||
| 457 | + void replaceAllocaLocalGeneric(CallBase *CI, EscapeScope &ES) { | ||
| 458 | + replaceWithRuntimeFunc(CI, false, true); | ||
| 459 | + CI->addRetAttr(Attribute::NoAlias); | ||
| 460 | + setMetadata(CI, "new_gen"); | ||
| 461 | + } | ||
| 462 | + | ||
| 412 | void replaceIsReference(CallBase *CI) { | 463 | void replaceIsReference(CallBase *CI) { |
| 413 | IRBuilder<> IRB(CI); | 464 | IRBuilder<> IRB(CI); |
| 414 | Value *Arg = CI->getArgOperand(0)->stripPointerCasts(); | 465 | Value *Arg = CI->getArgOperand(0)->stripPointerCasts(); |
| @@ -751,6 +802,30 @@ public: | |||
| 751 | CI->addRetAttr(Attribute::NoAlias); | 802 | CI->addRetAttr(Attribute::NoAlias); |
| 752 | } | 803 | } |
| 753 | 804 | ||
| 805 | + void loweringMallocLocalObject(CallBase *&CI) { | ||
| 806 | + const MDNode *MD = CI->getMetadata("MallocType"); | ||
| 807 | + if (MD == nullptr) { | ||
| 808 | + setHeapMallocSizeAlign(CI); | ||
| 809 | + replaceWithRuntimeFunc(CI, false, true); | ||
| 810 | + } else { | ||
| 811 | + StringRef Ty = dyn_cast<MDString>(MD->getOperand(0).get())->getString(); | ||
| 812 | + if (Ty.equals("HasFinalizer")) { | ||
| 813 | + setHeapMallocSizeAlign(CI); | ||
| 814 | + replaceNewLocalFinalizerFunc(CI); | ||
| 815 | + } else if (Ty.equals("Future") || Ty.equals("Mutex") || | ||
| 816 | + Ty.equals("Monitor") || Ty.equals("WaitQueue")) { | ||
| 817 | + replaceFixedNewObject(CI); | ||
| 818 | + } else if (Ty.equals("WeakRef")) { | ||
| 819 | + setHeapMallocSizeAlign(CI); | ||
| 820 | + replaceNewLocalWeakRefFunc(CI); | ||
| 821 | + } else { | ||
| 822 | + setHeapMallocSizeAlign(CI); | ||
| 823 | + replaceWithRuntimeFunc(CI, false, true); | ||
| 824 | + } | ||
| 825 | + } | ||
| 826 | + CI->addRetAttr(Attribute::NoAlias); | ||
| 827 | + } | ||
| 828 | + | ||
| 754 | private: | 829 | private: |
| 755 | Module &M; | 830 | Module &M; |
| 756 | LLVMContext &C; | 831 | LLVMContext &C; |
| @@ -763,6 +838,8 @@ private: | |||
| 763 | Intrinsic::ID IID = CI->getIntrinsicID(); | 838 | Intrinsic::ID IID = CI->getIntrinsicID(); |
| 764 | if (IID == Intrinsic::cj_malloc_array) { | 839 | if (IID == Intrinsic::cj_malloc_array) { |
| 765 | return getMallocArrayFuncName(CI); | 840 | return getMallocArrayFuncName(CI); |
| 841 | + } else if (IID == Intrinsic::cj_malloc_local_array) { | ||
| 842 | + return getMallocLocalArrayFuncName(CI); | ||
| 766 | } else { | 843 | } else { |
| 767 | auto Itr = RuntimeMap.find(IID); | 844 | auto Itr = RuntimeMap.find(IID); |
| 768 | assert(Itr != RuntimeMap.end() && "Runtime function don`t exist."); | 845 | assert(Itr != RuntimeMap.end() && "Runtime function don`t exist."); |
| @@ -802,6 +879,38 @@ private: | |||
| 802 | return ""; | 879 | return ""; |
| 803 | } | 880 | } |
| 804 | 881 | ||
| 882 | + StringRef getMallocLocalArrayFuncName(CallBase *CI) const { | ||
| 883 | + Value *Arg0 = CI->getOperand(0); | ||
| 884 | + // gc.malloc.array(i64, bitcast arg1 to i8*) | ||
| 885 | + auto *CastExpr = dyn_cast<ConstantExpr>(Arg0); | ||
| 886 | + auto *KlassGV = dyn_cast<GlobalVariable>(CastExpr->getOperand(0)); | ||
| 887 | + TypeInfo ArrayKlass(KlassGV); | ||
| 888 | + | ||
| 889 | + Type *ET = ArrayKlass.getArrayElementType(); | ||
| 890 | + if (ET->isPointerTy()) { | ||
| 891 | + return "CJ_MCC_NewLocalObjArray"; | ||
| 892 | + } else if (ET->isStructTy() || ET->isArrayTy()) { | ||
| 893 | + return "CJ_MCC_NewLocalArray"; | ||
| 894 | + } else { | ||
| 895 | + unsigned Size = | ||
| 896 | + static_cast<unsigned>(DL.getTypeAllocSize(ET).getFixedSize()); | ||
| 897 | + switch (Size) { | ||
| 898 | + case 1: | ||
| 899 | + return "CJ_MCC_NewLocalArray8"; | ||
| 900 | + case 2: | ||
| 901 | + return "CJ_MCC_NewLocalArray16"; | ||
| 902 | + case 4: | ||
| 903 | + return "CJ_MCC_NewLocalArray32"; | ||
| 904 | + case 8: | ||
| 905 | + return "CJ_MCC_NewLocalArray64"; | ||
| 906 | + default: | ||
| 907 | + break; | ||
| 908 | + } | ||
| 909 | + } | ||
| 910 | + report_fatal_error("Unsupported element klass type for gc.malloc.array!"); | ||
| 911 | + return ""; | ||
| 912 | + } | ||
| 913 | + | ||
| 805 | Function *getOrInsertRuntimeFunc(CallBase *CI, bool GCLeafFunc, bool GCMalloc, | 914 | Function *getOrInsertRuntimeFunc(CallBase *CI, bool GCLeafFunc, bool GCMalloc, |
| 806 | FunctionType *FuncType = nullptr) { | 915 | FunctionType *FuncType = nullptr) { |
| 807 | StringRef Callee = getRuntimeFuncName(CI); | 916 | StringRef Callee = getRuntimeFuncName(CI); |
| @@ -1078,10 +1187,38 @@ static bool runtimeLoweringFunc(Function &F, CJIntrinsicLowering &Lowering) { | |||
| 1078 | CI->addRetAttr(Attribute::NoAlias); | 1187 | CI->addRetAttr(Attribute::NoAlias); |
| 1079 | Changed = true; | 1188 | Changed = true; |
| 1080 | break; | 1189 | break; |
| 1190 | + case Intrinsic::cj_malloc_local_object: { | ||
| 1191 | + Lowering.loweringMallocLocalObject(CI); | ||
| 1192 | + Changed = true; | ||
| 1193 | + break; | ||
| 1194 | + } | ||
| 1195 | + case Intrinsic::cj_alloca_local_generic: | ||
| 1196 | + Lowering.setHeapMallocSizeAlign(CI); | ||
| 1197 | + Lowering.replaceAllocaLocalGeneric(CI, ES); | ||
| 1198 | + Changed = true; | ||
| 1199 | + break; | ||
| 1200 | + case Intrinsic::cj_malloc_local_array: | ||
| 1201 | + Lowering.replaceNewArray(CI); | ||
| 1202 | + CI->addRetAttr(Attribute::NoAlias); | ||
| 1203 | + Changed = true; | ||
| 1204 | + break; | ||
| 1205 | + case Intrinsic::cj_malloc_local_array_generic: | ||
| 1206 | + Lowering.replaceWithRuntimeFunc(CI, false, true); | ||
| 1207 | + CI->addRetAttr(Attribute::NoAlias); | ||
| 1208 | + Changed = true; | ||
| 1209 | + break; | ||
| 1081 | case Intrinsic::cj_invoke_gc: | 1210 | case Intrinsic::cj_invoke_gc: |
| 1082 | Lowering.replaceWithRuntimeFunc(CI, false, false); | 1211 | Lowering.replaceWithRuntimeFunc(CI, false, false); |
| 1083 | Changed = true; | 1212 | Changed = true; |
| 1084 | break; | 1213 | break; |
| 1214 | + case Intrinsic::cj_maybe_local_write_ref: | ||
| 1215 | + case Intrinsic::cj_maybe_local_write_generic: | ||
| 1216 | + case Intrinsic::cj_demode_write_ref: | ||
| 1217 | + // GC leaf: the local-aware writes are exported as plain aliases without a | ||
| 1218 | + // callee-saved-register stub, so they cannot act as safepoints. | ||
| 1219 | + Lowering.replaceWithRuntimeFunc(CI, true, false); | ||
| 1220 | + Changed = true; | ||
| 1221 | + break; | ||
| 1085 | case Intrinsic::cj_division_check_sdiv: | 1222 | case Intrinsic::cj_division_check_sdiv: |
| 1086 | case Intrinsic::cj_division_check_udiv: | 1223 | case Intrinsic::cj_division_check_udiv: |
| 1087 | case Intrinsic::cj_division_check_srem: | 1224 | case Intrinsic::cj_division_check_srem: |
| @@ -1120,7 +1257,7 @@ PreservedAnalyses CJRuntimeLowering::run(Module &M, | |||
| 1120 | Changed = true; | 1257 | Changed = true; |
| 1121 | } | 1258 | } |
| 1122 | 1259 | ||
| 1123 | - if (!CJLTOOpt) { | 1260 | + if (!CJLTOOpt && !F.isDeclaration()) { |
| 1124 | Changed |= runtimeLoweringFunc(F, CJLowering); | 1261 | Changed |= runtimeLoweringFunc(F, CJLowering); |
| 1125 | } | 1262 | } |
| 1126 | } | 1263 | } |
| @@ -1159,7 +1296,7 @@ public: | |||
| 1159 | Changed = true; | 1296 | Changed = true; |
| 1160 | } | 1297 | } |
| 1161 | 1298 | ||
| 1162 | - if (!CJLTOOpt) { | 1299 | + if (!CJLTOOpt && !F.isDeclaration()) { |
| 1163 | Changed |= runtimeLoweringFunc(F, CJLowering); | 1300 | Changed |= runtimeLoweringFunc(F, CJLowering); |
| 1164 | } | 1301 | } |
| 1165 | } | 1302 | } |
| @@ -22,6 +22,7 @@ add_llvm_component_library(LLVMScalarOpts | |||
| 22 | CJGCInstrRestore.cpp | 22 | CJGCInstrRestore.cpp |
| 23 | CJSimpleOpt.cpp | 23 | CJSimpleOpt.cpp |
| 24 | CJGenericIntrinsicOpt.cpp | 24 | CJGenericIntrinsicOpt.cpp |
| 25 | + CJInsertRemoveLocalFinalizer.cpp | ||
| 25 | CJSimpleRangeAnalysis.cpp | 26 | CJSimpleRangeAnalysis.cpp |
| 26 | CJRSSCE.cpp | 27 | CJRSSCE.cpp |
| 27 | CJLoopFloatOpt.cpp | 28 | CJLoopFloatOpt.cpp |
| @@ -1018,7 +1018,8 @@ struct DSEState { | |||
| 1018 | if (ID == Intrinsic::cj_gcwrite_ref) | 1018 | if (ID == Intrinsic::cj_gcwrite_ref) |
| 1019 | return MemoryLocation::get(II); | 1019 | return MemoryLocation::get(II); |
| 1020 | if (ID == Intrinsic::cj_gcread_generic || | 1020 | if (ID == Intrinsic::cj_gcread_generic || |
| 1021 | - ID == Intrinsic::cj_assign_generic) | 1021 | + ID == Intrinsic::cj_assign_generic || |
| 1022 | + ID == Intrinsic::cj_assign_local_generic) | ||
| 1022 | return MemoryLocation::getAfter(II->getArgOperand(0)); | 1023 | return MemoryLocation::getAfter(II->getArgOperand(0)); |
| 1023 | if (ID == Intrinsic::cj_gcwrite_generic) | 1024 | if (ID == Intrinsic::cj_gcwrite_generic) |
| 1024 | return MemoryLocation::getAfter(II->getArgOperand(1)); | 1025 | return MemoryLocation::getAfter(II->getArgOperand(1)); |
| @@ -117,6 +117,7 @@ void llvm::initializeScalarOpts(PassRegistry &Registry) { | |||
| 117 | initializeCJBarrierSplitLegacyPassPass(Registry); | 117 | initializeCJBarrierSplitLegacyPassPass(Registry); |
| 118 | initializeCJLoopFloatOptLegacyPassPass(Registry); | 118 | initializeCJLoopFloatOptLegacyPassPass(Registry); |
| 119 | initializeCJRuntimeLoweringLegacyPassPass(Registry); | 119 | initializeCJRuntimeLoweringLegacyPassPass(Registry); |
| 120 | + initializeCJInsertRemoveLocalFinalizerLegacyPassPass(Registry); | ||
| 120 | initializeCangjieSpecificOptLegacyPassPass(Registry); | 121 | initializeCangjieSpecificOptLegacyPassPass(Registry); |
| 121 | initializeInsertCJTBAALegacyPassPass(Registry); | 122 | initializeInsertCJTBAALegacyPassPass(Registry); |
| 122 | initializeCJSimpleRangeAnalysisPass(Registry); | 123 | initializeCJSimpleRangeAnalysisPass(Registry); |
| @@ -0,0 +1,287 @@ | |||
| 1 | +; RUN: opt < %s -passes=cj-insert-remove-local-finalizer -S | FileCheck %s | ||
| 2 | + | ||
| 3 | +; Every CJ_MCC_AddLocalFinalizer must be paired with a | ||
| 4 | +; CJ_MCC_RemoveLocalFinalizer at the end of the object's live range -- on every | ||
| 5 | +; path, exactly once. These tests pin down the placement for ordinary control | ||
| 6 | +; flow; the exception-handling shapes live in exceptions.ll. | ||
| 7 | + | ||
| 8 | +%Cls = type { i8*, i64 } | ||
| 9 | + | ||
| 10 | +declare i8 addrspace(1)* @CJ_MCC_NewFinalizer(i8*, i32) | ||
| 11 | +declare void @CJ_MCC_AddLocalFinalizer(i8 addrspace(1)*) | ||
| 12 | +declare void @use(i8 addrspace(1)*) | ||
| 13 | +declare void @use_cls(%Cls addrspace(1)*) | ||
| 14 | +declare i1 @cond() | ||
| 15 | + | ||
| 16 | + | ||
| 17 | +; A heap finalizer is never registered as a local one, so it must be left alone. | ||
| 18 | + | ||
| 19 | +define void @heap_finalizer_untouched(i8* %ti) { | ||
| 20 | +; CHECK-LABEL: @heap_finalizer_untouched( | ||
| 21 | +; CHECK-NOT: @CJ_MCC_RemoveLocalFinalizer | ||
| 22 | +; CHECK: ret void | ||
| 23 | +; | ||
| 24 | + %obj = call i8 addrspace(1)* @CJ_MCC_NewFinalizer(i8* %ti, i32 32) | ||
| 25 | + call void @use(i8 addrspace(1)* %obj) | ||
| 26 | + ret void | ||
| 27 | +} | ||
| 28 | + | ||
| 29 | + | ||
| 30 | +; The registration itself is the only use: the object is dead as soon as it is | ||
| 31 | +; registered, so the Remove follows immediately. | ||
| 32 | + | ||
| 33 | +define void @dead_on_arrival(i8* %ti) { | ||
| 34 | +; CHECK-LABEL: @dead_on_arrival( | ||
| 35 | +; CHECK: call void @CJ_MCC_AddLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 36 | +; CHECK-NEXT: call void @CJ_MCC_RemoveLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 37 | +; CHECK-NEXT: ret void | ||
| 38 | +; | ||
| 39 | + %obj = call i8 addrspace(1)* @CJ_MCC_NewFinalizer(i8* %ti, i32 32) | ||
| 40 | + call void @CJ_MCC_AddLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 41 | + ret void | ||
| 42 | +} | ||
| 43 | + | ||
| 44 | + | ||
| 45 | +; The object is only used on one arm of the branch. The other arm never touches | ||
| 46 | +; it, but it is just as dead there, so it needs its own Remove. | ||
| 47 | + | ||
| 48 | +define void @diamond(i8* %ti, i1 %c) { | ||
| 49 | +; CHECK-LABEL: @diamond( | ||
| 50 | +; CHECK: call void @CJ_MCC_AddLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 51 | +; CHECK-NOT: @CJ_MCC_RemoveLocalFinalizer | ||
| 52 | +; CHECK: then: | ||
| 53 | +; CHECK-NEXT: call void @use(i8 addrspace(1)* %obj) | ||
| 54 | +; CHECK-NEXT: call void @CJ_MCC_RemoveLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 55 | +; CHECK: else: | ||
| 56 | +; CHECK-NEXT: call void @CJ_MCC_RemoveLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 57 | +; CHECK: exit: | ||
| 58 | +; CHECK-NEXT: ret void | ||
| 59 | +; | ||
| 60 | +entry: | ||
| 61 | + %obj = call i8 addrspace(1)* @CJ_MCC_NewFinalizer(i8* %ti, i32 32) | ||
| 62 | + call void @CJ_MCC_AddLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 63 | + br i1 %c, label %then, label %else | ||
| 64 | +then: | ||
| 65 | + call void @use(i8 addrspace(1)* %obj) | ||
| 66 | + br label %exit | ||
| 67 | +else: | ||
| 68 | + br label %exit | ||
| 69 | +exit: | ||
| 70 | + ret void | ||
| 71 | +} | ||
| 72 | + | ||
| 73 | + | ||
| 74 | +; The object flows into phi nodes. It stays alive until the last use of the | ||
| 75 | +; phis, and %merge is also reached from a path where the phis are *not* the | ||
| 76 | +; object -- inserting on that incoming edge as well would remove it twice. | ||
| 77 | + | ||
| 78 | +define void @phi_merge(i8* %ti, i1 %c, i8 addrspace(1)* %other) { | ||
| 79 | +; CHECK-LABEL: @phi_merge( | ||
| 80 | +; CHECK: then: | ||
| 81 | +; CHECK-NOT: @CJ_MCC_RemoveLocalFinalizer | ||
| 82 | +; CHECK: else: | ||
| 83 | +; CHECK-NOT: @CJ_MCC_RemoveLocalFinalizer | ||
| 84 | +; CHECK: merge: | ||
| 85 | +; CHECK: call void @use(i8 addrspace(1)* %p) | ||
| 86 | +; CHECK-NEXT: call void @use(i8 addrspace(1)* %q) | ||
| 87 | +; CHECK-NEXT: call void @CJ_MCC_RemoveLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 88 | +; CHECK-NEXT: ret void | ||
| 89 | +; | ||
| 90 | +entry: | ||
| 91 | + %obj = call i8 addrspace(1)* @CJ_MCC_NewFinalizer(i8* %ti, i32 32) | ||
| 92 | + call void @CJ_MCC_AddLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 93 | + br i1 %c, label %then, label %else | ||
| 94 | +then: | ||
| 95 | + br label %merge | ||
| 96 | +else: | ||
| 97 | + br label %merge | ||
| 98 | +merge: | ||
| 99 | + %p = phi i8 addrspace(1)* [ %obj, %then ], [ %other, %else ] | ||
| 100 | + %q = phi i8 addrspace(1)* [ %obj, %then ], [ %other, %else ] | ||
| 101 | + call void @use(i8 addrspace(1)* %p) | ||
| 102 | + call void @use(i8 addrspace(1)* %q) | ||
| 103 | + ret void | ||
| 104 | +} | ||
| 105 | + | ||
| 106 | + | ||
| 107 | +; Same, but the last phi of the block is not the one carrying the object. The | ||
| 108 | +; call must not land between the phi nodes -- that is invalid IR. | ||
| 109 | + | ||
| 110 | +define i32 @phi_not_last(i8* %ti, i1 %c, i8 addrspace(1)* %other, i32 %x) { | ||
| 111 | +; CHECK-LABEL: @phi_not_last( | ||
| 112 | +; CHECK: merge: | ||
| 113 | +; CHECK-NEXT: %p = phi i8 addrspace(1)* | ||
| 114 | +; CHECK-NEXT: %n = phi i32 | ||
| 115 | +; CHECK-NEXT: call void @use(i8 addrspace(1)* %p) | ||
| 116 | +; CHECK-NEXT: call void @CJ_MCC_RemoveLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 117 | +; CHECK-NEXT: ret i32 %n | ||
| 118 | +; | ||
| 119 | +entry: | ||
| 120 | + %obj = call i8 addrspace(1)* @CJ_MCC_NewFinalizer(i8* %ti, i32 32) | ||
| 121 | + call void @CJ_MCC_AddLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 122 | + br i1 %c, label %then, label %else | ||
| 123 | +then: | ||
| 124 | + br label %merge | ||
| 125 | +else: | ||
| 126 | + br label %merge | ||
| 127 | +merge: | ||
| 128 | + %p = phi i8 addrspace(1)* [ %obj, %then ], [ %other, %else ] | ||
| 129 | + %n = phi i32 [ %x, %then ], [ 0, %else ] | ||
| 130 | + call void @use(i8 addrspace(1)* %p) | ||
| 131 | + ret i32 %n | ||
| 132 | +} | ||
| 133 | + | ||
| 134 | + | ||
| 135 | +; A select may yield the object, so the object is live until the select's | ||
| 136 | +; result is dead. | ||
| 137 | + | ||
| 138 | +define void @select_derived(i8* %ti, i1 %c, i8 addrspace(1)* %other) { | ||
| 139 | +; CHECK-LABEL: @select_derived( | ||
| 140 | +; CHECK: %p = select i1 %c, i8 addrspace(1)* %obj, i8 addrspace(1)* %other | ||
| 141 | +; CHECK-NEXT: call void @use(i8 addrspace(1)* %p) | ||
| 142 | +; CHECK-NEXT: call void @CJ_MCC_RemoveLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 143 | +; | ||
| 144 | + %obj = call i8 addrspace(1)* @CJ_MCC_NewFinalizer(i8* %ti, i32 32) | ||
| 145 | + call void @CJ_MCC_AddLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 146 | + %p = select i1 %c, i8 addrspace(1)* %obj, i8 addrspace(1)* %other | ||
| 147 | + call void @use(i8 addrspace(1)* %p) | ||
| 148 | + ret void | ||
| 149 | +} | ||
| 150 | + | ||
| 151 | + | ||
| 152 | +; The object is normally cast to its class type before being used, so a use of | ||
| 153 | +; the bitcast keeps the object alive. | ||
| 154 | + | ||
| 155 | +define void @bitcast_derived(i8* %ti) { | ||
| 156 | +; CHECK-LABEL: @bitcast_derived( | ||
| 157 | +; CHECK: %bc = bitcast i8 addrspace(1)* %obj to %Cls addrspace(1)* | ||
| 158 | +; CHECK-NEXT: call void @use_cls(%Cls addrspace(1)* %bc) | ||
| 159 | +; CHECK-NEXT: call void @CJ_MCC_RemoveLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 160 | +; | ||
| 161 | + %obj = call i8 addrspace(1)* @CJ_MCC_NewFinalizer(i8* %ti, i32 32) | ||
| 162 | + call void @CJ_MCC_AddLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 163 | + %bc = bitcast i8 addrspace(1)* %obj to %Cls addrspace(1)* | ||
| 164 | + call void @use_cls(%Cls addrspace(1)* %bc) | ||
| 165 | + ret void | ||
| 166 | +} | ||
| 167 | + | ||
| 168 | + | ||
| 169 | +; Created before the loop and used inside it: the back edge keeps it live | ||
| 170 | +; through the whole body, so the Remove belongs on the exit edge. | ||
| 171 | + | ||
| 172 | +define void @loop_use(i8* %ti) { | ||
| 173 | +; CHECK-LABEL: @loop_use( | ||
| 174 | +; CHECK: head: | ||
| 175 | +; CHECK-NEXT: call void @use(i8 addrspace(1)* %obj) | ||
| 176 | +; CHECK-NOT: @CJ_MCC_RemoveLocalFinalizer | ||
| 177 | +; CHECK: exit: | ||
| 178 | +; CHECK-NEXT: call void @CJ_MCC_RemoveLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 179 | +; CHECK-NEXT: ret void | ||
| 180 | +; | ||
| 181 | +entry: | ||
| 182 | + %obj = call i8 addrspace(1)* @CJ_MCC_NewFinalizer(i8* %ti, i32 32) | ||
| 183 | + call void @CJ_MCC_AddLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 184 | + br label %head | ||
| 185 | +head: | ||
| 186 | + call void @use(i8 addrspace(1)* %obj) | ||
| 187 | + %c = call i1 @cond() | ||
| 188 | + br i1 %c, label %head, label %exit | ||
| 189 | +exit: | ||
| 190 | + ret void | ||
| 191 | +} | ||
| 192 | + | ||
| 193 | + | ||
| 194 | +; Created *inside* the loop: a fresh object per iteration, each registered and | ||
| 195 | +; unregistered within its own iteration. | ||
| 196 | + | ||
| 197 | +define void @loop_new(i8* %ti) { | ||
| 198 | +; CHECK-LABEL: @loop_new( | ||
| 199 | +; CHECK: head: | ||
| 200 | +; CHECK: call void @CJ_MCC_AddLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 201 | +; CHECK-NEXT: call void @use(i8 addrspace(1)* %obj) | ||
| 202 | +; CHECK-NEXT: call void @CJ_MCC_RemoveLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 203 | +; CHECK: exit: | ||
| 204 | +; CHECK-NEXT: ret void | ||
| 205 | +; | ||
| 206 | +entry: | ||
| 207 | + br label %head | ||
| 208 | +head: | ||
| 209 | + %obj = call i8 addrspace(1)* @CJ_MCC_NewFinalizer(i8* %ti, i32 32) | ||
| 210 | + call void @CJ_MCC_AddLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 211 | + call void @use(i8 addrspace(1)* %obj) | ||
| 212 | + %c = call i1 @cond() | ||
| 213 | + br i1 %c, label %head, label %exit | ||
| 214 | +exit: | ||
| 215 | + ret void | ||
| 216 | +} | ||
| 217 | + | ||
| 218 | + | ||
| 219 | +; Stored into a local variable and read back: the object is still alive after | ||
| 220 | +; the store, so the Remove goes after the use of the loaded value. | ||
| 221 | + | ||
| 222 | +define void @slot_roundtrip(i8* %ti) { | ||
| 223 | +; CHECK-LABEL: @slot_roundtrip( | ||
| 224 | +; CHECK: store i8 addrspace(1)* %obj, i8 addrspace(1)** %slot | ||
| 225 | +; CHECK-NOT: @CJ_MCC_RemoveLocalFinalizer | ||
| 226 | +; CHECK: %l = load i8 addrspace(1)*, i8 addrspace(1)** %slot | ||
| 227 | +; CHECK-NEXT: call void @use(i8 addrspace(1)* %l) | ||
| 228 | +; CHECK-NEXT: call void @CJ_MCC_RemoveLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 229 | +; | ||
| 230 | + %slot = alloca i8 addrspace(1)* | ||
| 231 | + %obj = call i8 addrspace(1)* @CJ_MCC_NewFinalizer(i8* %ti, i32 32) | ||
| 232 | + call void @CJ_MCC_AddLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 233 | + store i8 addrspace(1)* %obj, i8 addrspace(1)** %slot | ||
| 234 | + %l = load i8 addrspace(1)*, i8 addrspace(1)** %slot | ||
| 235 | + call void @use(i8 addrspace(1)* %l) | ||
| 236 | + ret void | ||
| 237 | +} | ||
| 238 | + | ||
| 239 | + | ||
| 240 | +; The load is fed by a store from either arm, so no single store defines it. | ||
| 241 | +; Tracking the slot rather than an individual store keeps the object alive. | ||
| 242 | + | ||
| 243 | +define void @slot_memory_phi(i8* %ti, i1 %c) { | ||
| 244 | +; CHECK-LABEL: @slot_memory_phi( | ||
| 245 | +; CHECK: a: | ||
| 246 | +; CHECK-NEXT: store i8 addrspace(1)* %obj, i8 addrspace(1)** %slot | ||
| 247 | +; CHECK-NOT: @CJ_MCC_RemoveLocalFinalizer | ||
| 248 | +; CHECK: b: | ||
| 249 | +; CHECK-NEXT: store i8 addrspace(1)* %obj, i8 addrspace(1)** %slot | ||
| 250 | +; CHECK-NOT: @CJ_MCC_RemoveLocalFinalizer | ||
| 251 | +; CHECK: m: | ||
| 252 | +; CHECK-NEXT: %l = load i8 addrspace(1)*, i8 addrspace(1)** %slot | ||
| 253 | +; CHECK-NEXT: call void @use(i8 addrspace(1)* %l) | ||
| 254 | +; CHECK-NEXT: call void @CJ_MCC_RemoveLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 255 | +; | ||
| 256 | +entry: | ||
| 257 | + %slot = alloca i8 addrspace(1)* | ||
| 258 | + %obj = call i8 addrspace(1)* @CJ_MCC_NewFinalizer(i8* %ti, i32 32) | ||
| 259 | + call void @CJ_MCC_AddLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 260 | + br i1 %c, label %a, label %b | ||
| 261 | +a: | ||
| 262 | + store i8 addrspace(1)* %obj, i8 addrspace(1)** %slot | ||
| 263 | + br label %m | ||
| 264 | +b: | ||
| 265 | + store i8 addrspace(1)* %obj, i8 addrspace(1)** %slot | ||
| 266 | + br label %m | ||
| 267 | +m: | ||
| 268 | + %l = load i8 addrspace(1)*, i8 addrspace(1)** %slot | ||
| 269 | + call void @use(i8 addrspace(1)* %l) | ||
| 270 | + ret void | ||
| 271 | +} | ||
| 272 | + | ||
| 273 | + | ||
| 274 | +; Stored into a heap field: the object outlives this function through memory we | ||
| 275 | +; cannot follow, so it is left registered rather than unregistered too early. | ||
| 276 | + | ||
| 277 | +define void @escape_to_heap(i8* %ti, i8 addrspace(1)* addrspace(1)* %field) { | ||
| 278 | +; CHECK-LABEL: @escape_to_heap( | ||
| 279 | +; CHECK: call void @CJ_MCC_AddLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 280 | +; CHECK-NOT: @CJ_MCC_RemoveLocalFinalizer | ||
| 281 | +; CHECK: ret void | ||
| 282 | +; | ||
| 283 | + %obj = call i8 addrspace(1)* @CJ_MCC_NewFinalizer(i8* %ti, i32 32) | ||
| 284 | + call void @CJ_MCC_AddLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 285 | + store i8 addrspace(1)* %obj, i8 addrspace(1)* addrspace(1)* %field | ||
| 286 | + ret void | ||
| 287 | +} | ||
| @@ -0,0 +1,121 @@ | |||
| 1 | +; RUN: opt < %s -passes=cj-insert-remove-local-finalizer -S | FileCheck %s | ||
| 2 | + | ||
| 3 | +; Unwinding out of a local finalizer's live range is just another way for it to | ||
| 4 | +; die, and a landing pad shared by several invokes must still be unregistered | ||
| 5 | +; only once. | ||
| 6 | +; | ||
| 7 | +; The landing pads here are `landingpad token`, which is what the Cangjie | ||
| 8 | +; frontend emits and what CJRewriteStatepoint requires -- it uses the landing | ||
| 9 | +; pad as the token operand of the exceptional gc.relocates it generates. | ||
| 10 | + | ||
| 11 | +declare i8 addrspace(1)* @CJ_MCC_NewFinalizer(i8*, i32) | ||
| 12 | +declare void @CJ_MCC_AddLocalFinalizer(i8 addrspace(1)*) | ||
| 13 | +declare void @use(i8 addrspace(1)*) | ||
| 14 | +declare void @mayThrow() | ||
| 15 | +declare void @cleanupAction() | ||
| 16 | +declare i32 @personality_function() | ||
| 17 | + | ||
| 18 | + | ||
| 19 | +; The object survives the invoke on the normal path but is dead on the unwind | ||
| 20 | +; path, which never mentions it. Without a Remove in the landing pad the | ||
| 21 | +; registration would leak whenever the call throws. | ||
| 22 | + | ||
| 23 | +define void @live_across_invoke(i8* %ti) gc "cangjie" personality i32 ()* @personality_function { | ||
| 24 | +; CHECK-LABEL: @live_across_invoke( | ||
| 25 | +; CHECK: invoke void @mayThrow() | ||
| 26 | +; CHECK: cont: | ||
| 27 | +; CHECK-NEXT: call void @use(i8 addrspace(1)* %obj) | ||
| 28 | +; CHECK-NEXT: call void @CJ_MCC_RemoveLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 29 | +; CHECK-NEXT: ret void | ||
| 30 | +; CHECK: lpad: | ||
| 31 | +; CHECK-NEXT: landingpad token | ||
| 32 | +; CHECK-NEXT: cleanup | ||
| 33 | +; CHECK-NEXT: call void @CJ_MCC_RemoveLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 34 | +; CHECK-NEXT: call void @cleanupAction() | ||
| 35 | +; | ||
| 36 | +entry: | ||
| 37 | + %obj = call i8 addrspace(1)* @CJ_MCC_NewFinalizer(i8* %ti, i32 32) | ||
| 38 | + call void @CJ_MCC_AddLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 39 | + invoke void @mayThrow() to label %cont unwind label %lpad | ||
| 40 | +cont: | ||
| 41 | + call void @use(i8 addrspace(1)* %obj) | ||
| 42 | + ret void | ||
| 43 | +lpad: | ||
| 44 | + %l = landingpad token cleanup | ||
| 45 | + call void @cleanupAction() | ||
| 46 | + ret void | ||
| 47 | +} | ||
| 48 | + | ||
| 49 | + | ||
| 50 | +; Two invokes consume the object and share both successors. Every edge into | ||
| 51 | +; each successor is dead, so a single Remove per successor is enough -- one per | ||
| 52 | +; incoming edge would unregister the object twice. | ||
| 53 | + | ||
| 54 | +define void @shared_landing_pad(i8* %ti, i1 %c) gc "cangjie" personality i32 ()* @personality_function { | ||
| 55 | +; CHECK-LABEL: @shared_landing_pad( | ||
| 56 | +; CHECK: exit: | ||
| 57 | +; CHECK-NEXT: call void @CJ_MCC_RemoveLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 58 | +; CHECK-NEXT: ret void | ||
| 59 | +; CHECK: lpad: | ||
| 60 | +; CHECK-NEXT: landingpad token | ||
| 61 | +; CHECK-NEXT: cleanup | ||
| 62 | +; CHECK-NEXT: call void @CJ_MCC_RemoveLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 63 | +; CHECK-NEXT: call void @cleanupAction() | ||
| 64 | +; | ||
| 65 | +entry: | ||
| 66 | + %obj = call i8 addrspace(1)* @CJ_MCC_NewFinalizer(i8* %ti, i32 32) | ||
| 67 | + call void @CJ_MCC_AddLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 68 | + br i1 %c, label %b1, label %b2 | ||
| 69 | +b1: | ||
| 70 | + invoke void @use(i8 addrspace(1)* %obj) to label %exit unwind label %lpad | ||
| 71 | +b2: | ||
| 72 | + invoke void @use(i8 addrspace(1)* %obj) to label %exit unwind label %lpad | ||
| 73 | +exit: | ||
| 74 | + ret void | ||
| 75 | +lpad: | ||
| 76 | + %l = landingpad token cleanup | ||
| 77 | + call void @cleanupAction() | ||
| 78 | + ret void | ||
| 79 | +} | ||
| 80 | + | ||
| 81 | + | ||
| 82 | +; Here the object only exists on the %b1 path, while both successors are also | ||
| 83 | +; reached from %b2. The Remove cannot go into a shared successor -- %obj does | ||
| 84 | +; not dominate it -- so both critical edges out of %b1 are split, the unwind one | ||
| 85 | +; by cloning the landing pad. | ||
| 86 | + | ||
| 87 | +define void @split_critical_edges(i8* %ti, i1 %c) gc "cangjie" personality i32 ()* @personality_function { | ||
| 88 | +; CHECK-LABEL: @split_critical_edges( | ||
| 89 | +; CHECK: b1: | ||
| 90 | +; CHECK: call void @CJ_MCC_AddLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 91 | +; CHECK-NEXT: invoke void @use(i8 addrspace(1)* %obj) | ||
| 92 | +; CHECK-NEXT: to label %[[NORMAL:.*]] unwind label %[[UNWIND:.*]] | ||
| 93 | +; CHECK: [[NORMAL]]: | ||
| 94 | +; CHECK-NEXT: call void @CJ_MCC_RemoveLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 95 | +; CHECK-NEXT: br label %exit | ||
| 96 | +; CHECK: b2: | ||
| 97 | +; CHECK-NEXT: invoke void @mayThrow() | ||
| 98 | +; CHECK-NOT: @CJ_MCC_RemoveLocalFinalizer | ||
| 99 | +; CHECK: exit: | ||
| 100 | +; CHECK-NEXT: ret void | ||
| 101 | +; CHECK: [[UNWIND]]: | ||
| 102 | +; CHECK-NEXT: landingpad token | ||
| 103 | +; CHECK-NEXT: cleanup | ||
| 104 | +; CHECK-NEXT: call void @CJ_MCC_RemoveLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 105 | +; CHECK-NOT: call void @CJ_MCC_RemoveLocalFinalizer | ||
| 106 | +; | ||
| 107 | +entry: | ||
| 108 | + br i1 %c, label %b1, label %b2 | ||
| 109 | +b1: | ||
| 110 | + %obj = call i8 addrspace(1)* @CJ_MCC_NewFinalizer(i8* %ti, i32 32) | ||
| 111 | + call void @CJ_MCC_AddLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 112 | + invoke void @use(i8 addrspace(1)* %obj) to label %exit unwind label %lpad | ||
| 113 | +b2: | ||
| 114 | + invoke void @mayThrow() to label %exit unwind label %lpad | ||
| 115 | +exit: | ||
| 116 | + ret void | ||
| 117 | +lpad: | ||
| 118 | + %l = landingpad token cleanup | ||
| 119 | + call void @cleanupAction() | ||
| 120 | + ret void | ||
| 121 | +} | ||
| @@ -0,0 +1,74 @@ | |||
| 1 | +; Mirrors the real pass ordering: CJRuntimeLowering emits the registration, the | ||
| 2 | +; optimizer runs, and only then does this pass pair it up. The point is that | ||
| 3 | +; CJ_MCC_AddLocalFinalizer survives optimization and is still pairable, and that | ||
| 4 | +; the pass copes with the optimized shapes (promoted slots, threaded branches) | ||
| 5 | +; rather than the raw frontend ones. | ||
| 6 | +; | ||
| 7 | +; RUN: opt < %s -S \ | ||
| 8 | +; RUN: -passes='cj-runtime-lowering,function(sroa,instcombine,simplifycfg),function(cj-insert-remove-local-finalizer)' \ | ||
| 9 | +; RUN: | FileCheck %s | ||
| 10 | + | ||
| 11 | +%TypeInfo = type { i8*, i8, i8, i16, i32, i8*, i32, i8, i8, i32*, i8*, i8*, i8*, i8*, i8*, i8* } | ||
| 12 | + | ||
| 13 | +declare i8 addrspace(1)* @llvm.cj.malloc.local.object(i8*, i32) | ||
| 14 | +declare void @use(i8 addrspace(1)*) | ||
| 15 | +declare void @mayThrow() | ||
| 16 | +declare void @cleanupAction() | ||
| 17 | +declare i32 @personality_function() | ||
| 18 | + | ||
| 19 | + | ||
| 20 | +; The local variable slot is promoted by SROA, so by the time this pass runs the | ||
| 21 | +; object is plain SSA and the whole diamond has collapsed: one registration, one | ||
| 22 | +; unregistration. | ||
| 23 | + | ||
| 24 | +define void @slot_promoted_away(i8* %ti, i1 %c) gc "cangjie" { | ||
| 25 | +; CHECK-LABEL: @slot_promoted_away( | ||
| 26 | +; CHECK: call void @CJ_MCC_AddLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 27 | +; CHECK-NEXT: call void @use(i8 addrspace(1)* %obj) | ||
| 28 | +; CHECK-NEXT: call void @CJ_MCC_RemoveLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 29 | +; CHECK-NEXT: ret void | ||
| 30 | +; | ||
| 31 | +entry: | ||
| 32 | + %slot = alloca i8 addrspace(1)* | ||
| 33 | + %obj = call noalias i8 addrspace(1)* @llvm.cj.malloc.local.object(i8* %ti, i32 32), !MallocType !0 | ||
| 34 | + store i8 addrspace(1)* %obj, i8 addrspace(1)** %slot | ||
| 35 | + br i1 %c, label %a, label %b | ||
| 36 | +a: | ||
| 37 | + br label %m | ||
| 38 | +b: | ||
| 39 | + br label %m | ||
| 40 | +m: | ||
| 41 | + %l = load i8 addrspace(1)*, i8 addrspace(1)** %slot | ||
| 42 | + call void @use(i8 addrspace(1)* %l) | ||
| 43 | + ret void | ||
| 44 | +} | ||
| 45 | + | ||
| 46 | + | ||
| 47 | +; Optimization does not disturb the exception edges: the object is still | ||
| 48 | +; unregistered on both the normal and the unwind path, once each. | ||
| 49 | + | ||
| 50 | +define void @eh_survives_opt(i8* %ti) gc "cangjie" personality i32 ()* @personality_function { | ||
| 51 | +; CHECK-LABEL: @eh_survives_opt( | ||
| 52 | +; CHECK: call void @CJ_MCC_AddLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 53 | +; CHECK: invoke void @mayThrow() | ||
| 54 | +; CHECK: call void @use(i8 addrspace(1)* %obj) | ||
| 55 | +; CHECK-NEXT: call void @CJ_MCC_RemoveLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 56 | +; CHECK: landingpad token | ||
| 57 | +; CHECK-NEXT: cleanup | ||
| 58 | +; CHECK-NEXT: call void @CJ_MCC_RemoveLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 59 | +; CHECK-NEXT: call void @cleanupAction() | ||
| 60 | +; CHECK-NOT: call void @CJ_MCC_RemoveLocalFinalizer | ||
| 61 | +; | ||
| 62 | +entry: | ||
| 63 | + %obj = call noalias i8 addrspace(1)* @llvm.cj.malloc.local.object(i8* %ti, i32 32), !MallocType !0 | ||
| 64 | + invoke void @mayThrow() to label %cont unwind label %lpad | ||
| 65 | +cont: | ||
| 66 | + call void @use(i8 addrspace(1)* %obj) | ||
| 67 | + ret void | ||
| 68 | +lpad: | ||
| 69 | + %l = landingpad token cleanup | ||
| 70 | + call void @cleanupAction() | ||
| 71 | + ret void | ||
| 72 | +} | ||
| 73 | + | ||
| 74 | +!0 = !{!"HasFinalizer"} | ||
| @@ -0,0 +1,55 @@ | |||
| 1 | +; RUN: opt < %s -passes=cj-runtime-lowering -S | FileCheck %s | ||
| 2 | + | ||
| 3 | +; llvm.cj.demode.write.ref is emitted for a store into a `demode` instance field. A demoded | ||
| 4 | +; field always has mode `~local`, so the stored value is statically known to be on the heap, | ||
| 5 | +; while the owning object may still live in a local region -- i.e. this explicitly creates a | ||
| 6 | +; region -> heap edge. It must be lowered to a plain call to CJ_MCC_DemodeWriteRef. | ||
| 7 | +; | ||
| 8 | +; Same two properties as maybe_local_write_ref.ll: | ||
| 9 | +; 1. The argument order (obj, field, value) must be preserved -- it matches the runtime ABI | ||
| 10 | +; and differs from llvm.cj.gcwrite.ref, which is (value, obj, field). | ||
| 11 | +; 2. The lowering must happen in this pass rather than in CJBarrierLowering, whose fast path | ||
| 12 | +; would replace the barrier with a plain store when the GC phase is idle, silently dropping | ||
| 13 | +; the local-GC-root registration. | ||
| 14 | +; | ||
| 15 | +; It is kept separate from llvm.cj.maybe.local.write.ref so the runtime does not have to | ||
| 16 | +; re-check whether the stored value is region-allocated. | ||
| 17 | + | ||
| 18 | +declare void @llvm.cj.demode.write.ref(i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)*, i8 addrspace(1)*) | ||
| 19 | +declare void @llvm.cj.maybe.local.write.ref(i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)*, i8 addrspace(1)*) | ||
| 20 | +declare void @llvm.cj.gcwrite.ref(i8 addrspace(1)*, i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)*) | ||
| 21 | + | ||
| 22 | +define void @demode_write(i8 addrspace(1)* %obj, i8 addrspace(1)* addrspace(1)* %field, i8 addrspace(1)* %value) gc "cangjie" { | ||
| 23 | +; CHECK-LABEL: define void @demode_write | ||
| 24 | +; CHECK: call void @CJ_MCC_DemodeWriteRef(i8 addrspace(1)* %obj, i8 addrspace(1)* addrspace(1)* %field, i8 addrspace(1)* %value) | ||
| 25 | +; CHECK-NOT: llvm.cj.demode.write.ref | ||
| 26 | +entry: | ||
| 27 | + call void @llvm.cj.demode.write.ref(i8 addrspace(1)* %obj, i8 addrspace(1)* addrspace(1)* %field, i8 addrspace(1)* %value) | ||
| 28 | + ret void | ||
| 29 | +} | ||
| 30 | + | ||
| 31 | +; The two modal barriers must stay distinct runtime calls. | ||
| 32 | +define void @both_modal_barriers(i8 addrspace(1)* %obj, i8 addrspace(1)* addrspace(1)* %field, i8 addrspace(1)* %value) gc "cangjie" { | ||
| 33 | +; CHECK-LABEL: define void @both_modal_barriers | ||
| 34 | +; CHECK: call void @CJ_MCC_MaybeLocalWriteRef | ||
| 35 | +; CHECK: call void @CJ_MCC_DemodeWriteRef | ||
| 36 | +entry: | ||
| 37 | + call void @llvm.cj.maybe.local.write.ref(i8 addrspace(1)* %obj, i8 addrspace(1)* addrspace(1)* %field, i8 addrspace(1)* %value) | ||
| 38 | + call void @llvm.cj.demode.write.ref(i8 addrspace(1)* %obj, i8 addrspace(1)* addrspace(1)* %field, i8 addrspace(1)* %value) | ||
| 39 | + ret void | ||
| 40 | +} | ||
| 41 | + | ||
| 42 | +; An ordinary reference write barrier is left untouched by this pass; it is lowered later, by | ||
| 43 | +; CJBarrierLowering. | ||
| 44 | +define void @ordinary_write(i8 addrspace(1)* %obj, i8 addrspace(1)* addrspace(1)* %field, i8 addrspace(1)* %value) gc "cangjie" { | ||
| 45 | +; CHECK-LABEL: define void @ordinary_write | ||
| 46 | +; CHECK: call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %value, i8 addrspace(1)* %obj, i8 addrspace(1)* addrspace(1)* %field) | ||
| 47 | +entry: | ||
| 48 | + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %value, i8 addrspace(1)* %obj, i8 addrspace(1)* addrspace(1)* %field) | ||
| 49 | + ret void | ||
| 50 | +} | ||
| 51 | + | ||
| 52 | +; The runtime function must be declared as a GC leaf: CJ_MCC_DemodeWriteRef is exported as a | ||
| 53 | +; plain alias without a callee-saved-register stub, so it cannot act as a safepoint. | ||
| 54 | +; CHECK: declare void @CJ_MCC_DemodeWriteRef(i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)*, i8 addrspace(1)*) #[[ATTR:[0-9]+]] | ||
| 55 | +; CHECK: attributes #[[ATTR]] = {{{.*}}"gc-leaf-function"{{.*}}} | ||
| @@ -0,0 +1,40 @@ | |||
| 1 | +; RUN: opt < %s -passes=cj-runtime-lowering -S | FileCheck %s | ||
| 2 | + | ||
| 3 | +; llvm.cj.maybe.local.write.ref is emitted for a store into an instance field whose owning | ||
| 4 | +; object may live in a local region (`this` inside `init(this @local?)`). It must be lowered | ||
| 5 | +; to a plain call to CJ_MCC_MaybeLocalWriteRef. | ||
| 6 | +; | ||
| 7 | +; Two properties matter here: | ||
| 8 | +; 1. The argument order (obj, field, value) must be preserved. It deliberately differs from | ||
| 9 | +; llvm.cj.gcwrite.ref, which is (value, obj, field), because it matches the runtime ABI. | ||
| 10 | +; 2. The lowering must happen in this pass rather than in CJBarrierLowering. Although this is | ||
| 11 | +; a write barrier, it must additionally register the owning local object as a GC root | ||
| 12 | +; unconditionally, and CJBarrierLowering's fast path would replace the barrier with a plain | ||
| 13 | +; store whenever the GC phase is idle, silently dropping that registration. | ||
| 14 | + | ||
| 15 | +declare void @llvm.cj.maybe.local.write.ref(i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)*, i8 addrspace(1)*) | ||
| 16 | +declare void @llvm.cj.gcwrite.ref(i8 addrspace(1)*, i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)*) | ||
| 17 | + | ||
| 18 | +define void @maybe_local_write(i8 addrspace(1)* %obj, i8 addrspace(1)* addrspace(1)* %field, i8 addrspace(1)* %value) gc "cangjie" { | ||
| 19 | +; CHECK-LABEL: define void @maybe_local_write | ||
| 20 | +; CHECK: call void @CJ_MCC_MaybeLocalWriteRef(i8 addrspace(1)* %obj, i8 addrspace(1)* addrspace(1)* %field, i8 addrspace(1)* %value) | ||
| 21 | +; CHECK-NOT: llvm.cj.maybe.local.write.ref | ||
| 22 | +entry: | ||
| 23 | + call void @llvm.cj.maybe.local.write.ref(i8 addrspace(1)* %obj, i8 addrspace(1)* addrspace(1)* %field, i8 addrspace(1)* %value) | ||
| 24 | + ret void | ||
| 25 | +} | ||
| 26 | + | ||
| 27 | +; An ordinary reference write barrier is left untouched by this pass; it is lowered later, by | ||
| 28 | +; CJBarrierLowering. | ||
| 29 | +define void @ordinary_write(i8 addrspace(1)* %obj, i8 addrspace(1)* addrspace(1)* %field, i8 addrspace(1)* %value) gc "cangjie" { | ||
| 30 | +; CHECK-LABEL: define void @ordinary_write | ||
| 31 | +; CHECK: call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %value, i8 addrspace(1)* %obj, i8 addrspace(1)* addrspace(1)* %field) | ||
| 32 | +entry: | ||
| 33 | + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %value, i8 addrspace(1)* %obj, i8 addrspace(1)* addrspace(1)* %field) | ||
| 34 | + ret void | ||
| 35 | +} | ||
| 36 | + | ||
| 37 | +; The runtime function must be declared as a GC leaf: CJ_MCC_MaybeLocalWriteRef is exported as | ||
| 38 | +; a plain alias without a callee-saved-register stub, so it cannot act as a safepoint. | ||
| 39 | +; CHECK: declare void @CJ_MCC_MaybeLocalWriteRef(i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)*, i8 addrspace(1)*) #[[ATTR:[0-9]+]] | ||
| 40 | +; CHECK: attributes #[[ATTR]] = {{{.*}}"gc-leaf-function"{{.*}}} | ||
| @@ -2,16 +2,38 @@ | |||
| 2 | %TypeInfo = type { i8*, i8, i8, i16, i32, i8*, i32, i8, i8, i32*, i8*, i8*, i8*, i8*, i8*, i8* } | 2 | %TypeInfo = type { i8*, i8, i8, i16, i32, i8*, i32, i8, i8, i32*, i8*, i8*, i8*, i8*, i8*, i8* } |
| 3 | 3 | ||
| 4 | declare i8 addrspace(1)* @llvm.cj.malloc.object(i8*, i32) | 4 | declare i8 addrspace(1)* @llvm.cj.malloc.object(i8*, i32) |
| 5 | +declare i8 addrspace(1)* @llvm.cj.malloc.local.object(i8*, i32) | ||
| 5 | 6 | ||
| 6 | ; NewFinalier cannot add "cj-malloc" attribute, because it may call a finalier method when destructor performing. | 7 | ; NewFinalier cannot add "cj-malloc" attribute, because it may call a finalier method when destructor performing. |
| 7 | 8 | ||
| 9 | +; A heap finalizer is owned by the GC, so it is neither registered nor | ||
| 10 | +; unregistered as a local one. | ||
| 8 | define void @lower_new_finalier(i8* %ti, i8** %s) { | 11 | define void @lower_new_finalier(i8* %ti, i8** %s) { |
| 9 | ; CHECK-LABEL: @lower_new_finalier( | 12 | ; CHECK-LABEL: @lower_new_finalier( |
| 10 | -; CHECK: declare i8 addrspace(1)* @CJ_MCC_NewFinalizer(i8*, i32) #1 | 13 | +; CHECK: %obj = call noalias i8 addrspace(1)* @CJ_MCC_NewFinalizer(i8* %ti, i32 %{{[0-9]+}}) |
| 11 | -; CHECK: attributes #1 = { "cj-runtime" } | 14 | +; CHECK-NOT: @CJ_MCC_AddLocalFinalizer |
| 15 | +; CHECK-NOT: @CJ_MCC_RemoveLocalFinalizer | ||
| 16 | +; CHECK: ret void | ||
| 12 | ; | 17 | ; |
| 13 | %obj = call noalias i8 addrspace(1)* @llvm.cj.malloc.object(i8* %ti, i32 32), !MallocType !0 | 18 | %obj = call noalias i8 addrspace(1)* @llvm.cj.malloc.object(i8* %ti, i32 32), !MallocType !0 |
| 14 | ret void | 19 | ret void |
| 15 | } | 20 | } |
| 16 | 21 | ||
| 22 | +; A LocalMode finalizer object is registered right after it is created. The | ||
| 23 | +; matching CJ_MCC_RemoveLocalFinalizer is inserted later in the pipeline by | ||
| 24 | +; cj-insert-remove-local-finalizer, so it is not expected here. | ||
| 25 | +define void @lower_new_local_finalier(i8* %ti) { | ||
| 26 | +; CHECK-LABEL: @lower_new_local_finalier( | ||
| 27 | +; CHECK: %obj = call noalias i8 addrspace(1)* @CJ_MCC_NewFinalizer(i8* %ti, i32 %{{[0-9]+}}) | ||
| 28 | +; CHECK-NEXT: call void @CJ_MCC_AddLocalFinalizer(i8 addrspace(1)* %obj) | ||
| 29 | +; CHECK-NEXT: ret void | ||
| 30 | +; CHECK-NOT: @CJ_MCC_RemoveLocalFinalizer | ||
| 31 | +; | ||
| 32 | + %obj = call noalias i8 addrspace(1)* @llvm.cj.malloc.local.object(i8* %ti, i32 32), !MallocType !0 | ||
| 33 | + ret void | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | +; CHECK: declare i8 addrspace(1)* @CJ_MCC_NewFinalizer(i8*, i32) #[[RT:[0-9]+]] | ||
| 37 | +; CHECK: attributes #[[RT]] = { "cj-runtime" } | ||
| 38 | + | ||
| 17 | !0 = !{!"HasFinalizer"} | 39 | !0 = !{!"HasFinalizer"} |


[minor] 名单成对补了 NewObject / NewLocalObject,却没有给 NewFinalizer 配 NewLocalFinalizer
问题:本次把
isCangjieNewObjFunction()的名单从{CJ_MCC_NewObject, CJ_MCC_NewFinalizer}扩展为{CJ_MCC_NewObject, CJ_MCC_NewLocalObject, CJ_MCC_NewFinalizer}——给 NewObject 配了 local 变体,但同一名单里的 NewFinalizer 没有配CJ_MCC_NewLocalFinalizer,而该符号本次确实新增了(cangjie_runtime 的CompilerCalls.cpp与 8 个架构的CalleeSavedStub.S都有)。影响:该谓词有三个消费者——
lib/CodeGen/CJBarrierLowering.cpp:1121的isNewObj()(识别分配 statepoint)、lib/Transforms/Scalar/CJFillMetadata.cpp:548的isNewObjectCallSite()(本次未改动)、以及lib/Transforms/IPO/CJPartialEscapeAnalysis.cpp:470。第三处经核实不可达(该 pass 候选收集条件是startswith("CJ_MCC_NewObject"),local finalizer 进不去),但前两处若需要把 local finalizer 对象识别为「新分配对象」,当前名单会让它被漏掉,其屏障或类型元数据处理与非 local finalizer 不一致。建议:请确认
CJ_MCC_NewLocalFinalizer是否应当进入该名单。若应当,补上即可;若不应当(例如 local finalizer 的屏障/元数据由别处处理),建议在该函数上补一行注释说明为何 NewObject 需要 local 变体而 NewFinalizer 不需要,避免下一个人重复这个疑问。