diff --git a/flang/include/flang/Optimizer/Transforms/Passes.h b/flang/include/flang/Optimizer/Transforms/Passes.h index 30d97be3800c1..92bc7246eca70 100644 --- a/flang/include/flang/Optimizer/Transforms/Passes.h +++ b/flang/include/flang/Optimizer/Transforms/Passes.h @@ -75,8 +75,6 @@ std::unique_ptr createAlgebraicSimplificationPass(const mlir::GreedyRewriteConfig &config); std::unique_ptr createPolymorphicOpConversionPass(); -std::unique_ptr> -createOMPEarlyOutliningPass(); std::unique_ptr createOMPFunctionFilteringPass(); std::unique_ptr> createOMPMarkDeclareTargetPass(); diff --git a/flang/include/flang/Optimizer/Transforms/Passes.td b/flang/include/flang/Optimizer/Transforms/Passes.td index 6d211a535b53f..6e23b87b7e276 100644 --- a/flang/include/flang/Optimizer/Transforms/Passes.td +++ b/flang/include/flang/Optimizer/Transforms/Passes.td @@ -318,18 +318,6 @@ def LoopVersioning : Pass<"loop-versioning", "mlir::func::FuncOp"> { let dependentDialects = [ "fir::FIROpsDialect" ]; } -def OMPEarlyOutliningPass - : Pass<"omp-early-target-outlining", "mlir::ModuleOp"> { - let summary = "Outlines all target ops into separate functions"; - let description = [{ - This pass outlines all omp.target operations into individual functions. - It is invoked in the front end after the initial FIR has been constructed. - This pass is only needed when compiling for the target device to prevent - the optimizer to perform transforms across target region boundaries. - }]; - let constructor = "::fir::createOMPEarlyOutliningPass()"; -} - def OMPMarkDeclareTargetPass : Pass<"omp-mark-declare-target", "mlir::ModuleOp"> { let summary = "Marks all functions called by an OpenMP declare target function as declare target"; diff --git a/flang/include/flang/Tools/CLOptions.inc b/flang/include/flang/Tools/CLOptions.inc index 2ed716382feb4..c452c023b4a80 100644 --- a/flang/include/flang/Tools/CLOptions.inc +++ b/flang/include/flang/Tools/CLOptions.inc @@ -274,10 +274,8 @@ inline void createHLFIRToFIRPassPipeline( inline void createOpenMPFIRPassPipeline( mlir::PassManager &pm, bool isTargetDevice) { pm.addPass(fir::createOMPMarkDeclareTargetPass()); - if (isTargetDevice) { - pm.addPass(fir::createOMPEarlyOutliningPass()); + if (isTargetDevice) pm.addPass(fir::createOMPFunctionFilteringPass()); - } } #if !defined(FLANG_EXCLUDE_CODEGEN) diff --git a/flang/lib/Optimizer/Transforms/CMakeLists.txt b/flang/lib/Optimizer/Transforms/CMakeLists.txt index 98314fa7a2087..03b67104a93b5 100644 --- a/flang/lib/Optimizer/Transforms/CMakeLists.txt +++ b/flang/lib/Optimizer/Transforms/CMakeLists.txt @@ -17,7 +17,6 @@ add_flang_library(FIRTransforms AddDebugFoundation.cpp PolymorphicOpConversion.cpp LoopVersioning.cpp - OMPEarlyOutlining.cpp OMPFunctionFiltering.cpp OMPMarkDeclareTarget.cpp VScaleAttr.cpp diff --git a/flang/lib/Optimizer/Transforms/OMPEarlyOutlining.cpp b/flang/lib/Optimizer/Transforms/OMPEarlyOutlining.cpp deleted file mode 100644 index 92fbdd0bbf5d4..0000000000000 --- a/flang/lib/Optimizer/Transforms/OMPEarlyOutlining.cpp +++ /dev/null @@ -1,303 +0,0 @@ -#include "flang/Optimizer/Dialect/FIRDialect.h" -#include "flang/Optimizer/Dialect/FIROps.h" -#include "flang/Optimizer/Dialect/FIRType.h" -#include "flang/Optimizer/HLFIR/HLFIROps.h" -#include "flang/Optimizer/Support/InternalNames.h" -#include "flang/Optimizer/Transforms/Passes.h" -#include "mlir/Dialect/Func/IR/FuncOps.h" -#include "mlir/Dialect/LLVMIR/LLVMDialect.h" -#include "mlir/Dialect/OpenMP/OpenMPDialect.h" -#include "mlir/IR/BuiltinDialect.h" -#include "mlir/IR/BuiltinOps.h" -#include "mlir/IR/IRMapping.h" -#include "mlir/IR/Operation.h" -#include "mlir/IR/SymbolTable.h" -#include "mlir/Pass/Pass.h" -#include "mlir/Support/LLVM.h" -#include "mlir/Transforms/RegionUtils.h" -#include "llvm/Frontend/OpenMP/OMPIRBuilder.h" - -namespace fir { -#define GEN_PASS_DEF_OMPEARLYOUTLININGPASS -#include "flang/Optimizer/Transforms/Passes.h.inc" -} // namespace fir - -namespace { -class OMPEarlyOutliningPass - : public fir::impl::OMPEarlyOutliningPassBase { - - std::string getOutlinedFnName(llvm::StringRef parentName, unsigned count) { - return std::string(parentName) + "_omp_outline_" + std::to_string(count); - } - - // Given a value this function will iterate over an operators results - // and return the relevant index for the result the value corresponds to. - // There may be a simpler way to do this however. - static unsigned getResultIndex(mlir::Value value, mlir::Operation *op) { - for (unsigned i = 0; i < op->getNumResults(); ++i) { - if (op->getResult(i) == value) - return i; - } - return 0; - } - - static bool isAddressOfGlobalDeclareTarget(mlir::Value value) { - if (fir::AddrOfOp addressOfOp = - mlir::dyn_cast_if_present(value.getDefiningOp())) - if (fir::GlobalOp gOp = mlir::dyn_cast_if_present( - addressOfOp->getParentOfType().lookupSymbol( - addressOfOp.getSymbol()))) - if (auto declareTargetGlobal = - llvm::dyn_cast( - gOp.getOperation())) - if (declareTargetGlobal.isDeclareTarget()) - return true; - return false; - } - - // Currently used for cloning arguments that are nested. Should be - // extendable where required, perhaps via operation - // specialisation/overloading, if something needs specialised handling. - // NOTE: Results in duplication of some values that would otherwise be - // a single SSA value shared between operations, this is tidied up on - // lowering to some extent. - static mlir::Operation * - cloneArgAndChildren(mlir::OpBuilder &builder, mlir::Operation *op, - llvm::SetVector &inputs, - mlir::Block::BlockArgListType &newInputs) { - mlir::IRMapping valueMap; - for (mlir::Value opValue : op->getOperands()) { - if (opValue.getDefiningOp()) { - unsigned resIdx = getResultIndex(opValue, opValue.getDefiningOp()); - valueMap.map(opValue, - cloneArgAndChildren(builder, opValue.getDefiningOp(), - inputs, newInputs) - ->getResult(resIdx)); - } else { - for (auto inArg : llvm::zip(inputs, newInputs)) { - if (opValue == std::get<0>(inArg)) - valueMap.map(opValue, std::get<1>(inArg)); - } - } - } - - return builder.clone(*op, valueMap); - } - - static void cloneMapOpVariables(mlir::OpBuilder &builder, - mlir::IRMapping &valueMap, - mlir::IRMapping &mapInfoMap, - llvm::SetVector &inputs, - mlir::Block::BlockArgListType &newInputs, - mlir::Value varPtr) { - if (fir::BoxAddrOp boxAddrOp = - mlir::dyn_cast_if_present(varPtr.getDefiningOp())) { - mlir::Value newV = - cloneArgAndChildren(builder, boxAddrOp, inputs, newInputs) - ->getResult(0); - mapInfoMap.map(varPtr, newV); - valueMap.map(boxAddrOp, newV); - return; - } - - // Clone into the outlined function all hlfir.declare ops that define inputs - // to the target region and set up remapping of its inputs and outputs. - if (auto declareOp = mlir::dyn_cast_if_present( - varPtr.getDefiningOp())) { - auto clone = llvm::cast( - cloneArgAndChildren(builder, declareOp, inputs, newInputs)); - mlir::Value newBase = clone.getBase(); - mlir::Value newOrigBase = clone.getOriginalBase(); - mapInfoMap.map(varPtr, newOrigBase); - valueMap.map(declareOp.getBase(), newBase); - valueMap.map(declareOp.getOriginalBase(), newOrigBase); - return; - } - - if (isAddressOfGlobalDeclareTarget(varPtr)) { - fir::AddrOfOp addrOp = - mlir::dyn_cast(varPtr.getDefiningOp()); - mlir::Value newV = builder.clone(*addrOp)->getResult(0); - mapInfoMap.map(varPtr, newV); - valueMap.map(addrOp, newV); - return; - } - - for (auto inArg : llvm::zip(inputs, newInputs)) { - if (varPtr == std::get<0>(inArg)) - mapInfoMap.map(varPtr, std::get<1>(inArg)); - } - } - - mlir::func::FuncOp outlineTargetOp(mlir::OpBuilder &builder, - mlir::omp::TargetOp &targetOp, - mlir::func::FuncOp &parentFunc, - unsigned count) { - // NOTE: once implicit captures are handled appropriately in the initial - // PFT lowering if it is possible, we can remove the usage of - // getUsedValuesDefinedAbove and instead just iterate over the target op's - // operands (or just the map arguments) and perhaps refactor this function - // a little. - // Collect inputs - llvm::SetVector inputs; - mlir::Region &targetRegion = targetOp.getRegion(); - mlir::getUsedValuesDefinedAbove(targetRegion, inputs); - - // Collect all map info. Even non-used maps must be collected to avoid ICEs. - for (mlir::Value oper : targetOp->getOperands()) { - if (auto mapEntry = - mlir::dyn_cast(oper.getDefiningOp())) { - if (!inputs.contains(mapEntry.getVarPtr())) - inputs.insert(mapEntry.getVarPtr()); - } - } - - // Filter out declare-target and map entries which are specially handled - // at the moment, so we do not wish these to end up as function arguments - // which would just be more noise in the IR. - llvm::SmallVector blockArgs; - for (llvm::SetVector::iterator iter = inputs.begin(); iter != inputs.end();) { - if (mlir::isa_and_nonnull(iter->getDefiningOp()) || - isAddressOfGlobalDeclareTarget(*iter)) { - iter = inputs.erase(iter); - } else if (auto declareOp = mlir::dyn_cast_if_present( - iter->getDefiningOp())) { - // Gather hlfir.declare arguments to be added later, after the - // hlfir.declare operation itself has been removed as an input. - blockArgs.push_back(declareOp.getMemref()); - if (mlir::Value shape = declareOp.getShape()) - blockArgs.push_back(shape); - for (mlir::Value typeParam : declareOp.getTypeparams()) - blockArgs.push_back(typeParam); - iter = inputs.erase(iter); - } else { - ++iter; - } - } - - // Add function arguments to the list of inputs if they are used by an - // hlfir.declare operation. - for (mlir::Value arg : blockArgs) { - if (!arg.getDefiningOp() && !inputs.contains(arg)) - inputs.insert(arg); - } - - // Create new function and initialize - mlir::FunctionType funcType = builder.getFunctionType( - mlir::TypeRange(inputs.getArrayRef()), mlir::TypeRange()); - std::string parentName(parentFunc.getName()); - std::string funcName = getOutlinedFnName(parentName, count); - mlir::Location loc = targetOp.getLoc(); - mlir::func::FuncOp newFunc = - mlir::func::FuncOp::create(loc, funcName, funcType); - mlir::Block *entryBlock = newFunc.addEntryBlock(); - builder.setInsertionPointToStart(entryBlock); - mlir::Block::BlockArgListType newInputs = entryBlock->getArguments(); - - // Set the declare target information, the outlined function - // is always a host function. - if (auto parentDTOp = llvm::dyn_cast( - parentFunc.getOperation())) - if (auto newDTOp = llvm::dyn_cast( - newFunc.getOperation())) - newDTOp.setDeclareTarget(mlir::omp::DeclareTargetDeviceType::host, - parentDTOp.getDeclareTargetCaptureClause()); - - // Set the early outlining interface parent name - if (auto earlyOutlineOp = - llvm::dyn_cast( - newFunc.getOperation())) - earlyOutlineOp.setParentName(parentName); - - // The value map for the newly generated Target Operation, we must - // remap most of the input. - mlir::IRMapping valueMap; - - // Special handling for map, declare target and regular map variables - // are handled slightly differently for the moment, declare target has - // its addressOfOp cloned over, whereas we skip it for the regular map - // variables. We need knowledge of which global is linked to the map - // operation for declare target, whereas we aren't bothered for the - // regular map variables for the moment. We could treat both the same, - // however, cloning across the minimum for the moment to avoid - // optimisations breaking segments of the lowering seems prudent as this - // was the original intent of the pass. - for (mlir::Value oper : targetOp->getOperands()) { - if (auto mapEntry = - mlir::dyn_cast(oper.getDefiningOp())) { - mlir::IRMapping mapInfoMap; - for (mlir::Value bound : mapEntry.getBounds()) { - if (auto mapEntryBound = mlir::dyn_cast( - bound.getDefiningOp())) { - mapInfoMap.map(bound, cloneArgAndChildren(builder, mapEntryBound, - inputs, newInputs) - ->getResult(0)); - } - } - - cloneMapOpVariables(builder, valueMap, mapInfoMap, inputs, newInputs, - mapEntry.getVarPtr()); - - if (mapEntry.getVarPtrPtr()) - cloneMapOpVariables(builder, valueMap, mapInfoMap, inputs, newInputs, - mapEntry.getVarPtrPtr()); - - valueMap.map( - mapEntry, - builder.clone(*mapEntry.getOperation(), mapInfoMap)->getResult(0)); - } - } - - for (auto inArg : llvm::zip(inputs, newInputs)) - valueMap.map(std::get<0>(inArg), std::get<1>(inArg)); - - // Clone the target op into the new function - builder.clone(*(targetOp.getOperation()), valueMap); - - // Create return op - builder.create(loc); - - return newFunc; - } - - // Returns true if a target region was found in the function. - bool outlineTargetOps(mlir::OpBuilder &builder, - mlir::func::FuncOp &functionOp, - mlir::ModuleOp &moduleOp, - llvm::SmallVectorImpl &newFuncs) { - unsigned count = 0; - for (auto TargetOp : functionOp.getOps()) { - mlir::func::FuncOp outlinedFunc = - outlineTargetOp(builder, TargetOp, functionOp, count); - newFuncs.push_back(outlinedFunc); - count++; - } - return count > 0; - } - - void runOnOperation() override { - mlir::ModuleOp moduleOp = getOperation(); - mlir::MLIRContext *context = &getContext(); - mlir::OpBuilder builder(context); - llvm::SmallVector newFuncs; - - for (auto functionOp : - llvm::make_early_inc_range(moduleOp.getOps())) { - bool outlined = outlineTargetOps(builder, functionOp, moduleOp, newFuncs); - if (outlined) - functionOp.erase(); - } - - for (auto newFunc : newFuncs) - moduleOp.push_back(newFunc); - } -}; - -} // namespace - -namespace fir { -std::unique_ptr> -createOMPEarlyOutliningPass() { - return std::make_unique(); -} -} // namespace fir diff --git a/flang/test/Lower/OpenMP/FIR/omp-target-early-outlining.f90 b/flang/test/Lower/OpenMP/FIR/omp-target-early-outlining.f90 deleted file mode 100644 index 14bf911b3e541..0000000000000 --- a/flang/test/Lower/OpenMP/FIR/omp-target-early-outlining.f90 +++ /dev/null @@ -1,89 +0,0 @@ -!REQUIRES: amdgpu-registered-target - -!RUN: %flang_fc1 -triple amdgcn-amd-amdhsa -emit-fir -fopenmp -fopenmp-is-target-device %s -o - | FileCheck %s -!RUN: %flang_fc1 -triple x86_64-unknown-linux-gnu -emit-fir -fopenmp -fopenmp-is-target-device %s -o - | FileCheck %s -!RUN: bbc -emit-fir -fopenmp -fopenmp-is-target-device %s -o - | FileCheck %s -!RUN: bbc -emit-fir -fopenmp -fopenmp-is-gpu -fopenmp-is-target-device %s -o - | FileCheck %s - -!CHECK: func.func @_QPtarget_function - -!CHECK: func.func @_QPwrite_index_omp_outline_0(%[[ARG0:.*]]: !fir.ref) attributes {omp.declare_target = #omp.declaretarget, omp.outline_parent_name = "_QPwrite_index"} { -!CHECK-NEXT: %[[map_info0:.*]] = omp.map_info var_ptr(%[[ARG0]]{{.*}} -!CHECK-NEXT: omp.target map_entries(%[[map_info0]]{{.*}} { -!CHECK: %[[CONSTANT_VALUE_10:.*]] = arith.constant 10 : i32 -!CHECK: fir.store %[[CONSTANT_VALUE_10]] to %[[ARG0]] : !fir.ref -!CHECK: omp.terminator -!CHECK-NEXT: } -!CHECK-NEXT: return - -!CHECK: func.func @_QPwrite_index_omp_outline_1(%[[ARG1:.*]]: !fir.ref) attributes {omp.declare_target = #omp.declaretarget, omp.outline_parent_name = "_QPwrite_index"} { -!CHECK-NEXT: %[[map_info1:.*]] = omp.map_info var_ptr(%[[ARG1]]{{.*}} -!CHECK-NEXT: omp.target map_entries(%[[map_info1]]{{.*}} { -!CHECK: %[[CONSTANT_VALUE_20:.*]] = arith.constant 20 : i32 -!CHECK: fir.store %[[CONSTANT_VALUE_20]] to %[[ARG1]] : !fir.ref -!CHECK: omp.terminator -!CHECK-NEXT: } -!CHECK-NEXT: return - - -SUBROUTINE WRITE_INDEX(INT_ARRAY) - INTEGER :: INT_ARRAY(*) - INTEGER :: NEW_LEN -!$omp target map(from:new_len) - NEW_LEN = 10 -!$omp end target -!$omp target map(from:new_len) - NEW_LEN = 20 -!$omp end target - do INDEX_ = 1, NEW_LEN - INT_ARRAY(INDEX_) = INDEX_ - end do -end subroutine WRITE_INDEX - -SUBROUTINE TARGET_FUNCTION() -!$omp declare target -END - -!CHECK: func.func @_QParray_bounds_omp_outline_0(%[[ARG0:.*]]: !fir.ref, %[[ARG1:.*]]: !fir.ref>) attributes {omp.declare_target = #omp.declaretarget, omp.outline_parent_name = "_QParray_bounds"} { -!CHECK: %[[C1:.*]] = arith.constant 1 : index -!CHECK: %[[C4:.*]] = arith.constant 4 : index -!CHECK: %[[C1_0:.*]] = arith.constant 1 : index -!CHECK: %[[C1_1:.*]] = arith.constant 1 : index -!CHECK: %[[BOUNDS:.*]] = omp.bounds lower_bound(%[[C1]] : index) upper_bound(%[[C4]] : index) stride(%[[C1_1]] : index) start_idx(%[[C1_1]] : index) -!CHECK: %[[ENTRY:.*]] = omp.map_info var_ptr(%[[ARG1]] : !fir.ref>, !fir.array<10xi32>) map_clauses(tofrom) capture(ByRef) bounds(%[[BOUNDS]]) -> !fir.ref> {name = "sp_write(2:5)"} -!CHECK: omp.target map_entries(%[[ENTRY]] : !fir.ref>) { -!CHECK: %c2_i32 = arith.constant 2 : i32 -!CHECK: %2 = fir.convert %c2_i32 : (i32) -> index -!CHECK: %c5_i32 = arith.constant 5 : i32 -!CHECK: %3 = fir.convert %c5_i32 : (i32) -> index -!CHECK: %c1_2 = arith.constant 1 : index -!CHECK: %4 = fir.convert %2 : (index) -> i32 -!CHECK: %5:2 = fir.do_loop %arg2 = %2 to %3 step %c1_2 iter_args(%arg3 = %4) -> (index, i32) { -!CHECK: fir.store %arg3 to %[[ARG0]] : !fir.ref -!CHECK: %6 = fir.load %[[ARG0]] : !fir.ref -!CHECK: %7 = fir.load %[[ARG0]] : !fir.ref -!CHECK: %8 = fir.convert %7 : (i32) -> i64 -!CHECK: %c1_i64 = arith.constant 1 : i64 -!CHECK: %9 = arith.subi %8, %c1_i64 : i64 -!CHECK: %10 = fir.coordinate_of %[[ARG1]], %9 : (!fir.ref>, i64) -> !fir.ref -!CHECK: fir.store %6 to %10 : !fir.ref -!CHECK: %11 = arith.addi %arg2, %c1_2 : index -!CHECK: %12 = fir.convert %c1_2 : (index) -> i32 -!CHECK: %13 = fir.load %[[ARG0]] : !fir.ref -!CHECK: %14 = arith.addi %13, %12 : i32 -!CHECK: fir.result %11, %14 : index, i32 -!CHECK: } -!CHECK: fir.store %5#1 to %[[ARG0]] : !fir.ref -!CHECK: omp.terminator -!CHECK: } -!CHECK:return -!CHECK:} - -SUBROUTINE ARRAY_BOUNDS() - INTEGER :: sp_write(10) = (/0,0,0,0,0,0,0,0,0,0/) -!$omp target map(tofrom:sp_write(2:5)) - do i = 2, 5 - sp_write(i) = i - end do -!$omp end target -end subroutine ARRAY_BOUNDS diff --git a/flang/test/Lower/OpenMP/function-filtering-2.f90 b/flang/test/Lower/OpenMP/function-filtering-2.f90 index 8219be5ad1e40..17cd0d44c01b4 100644 --- a/flang/test/Lower/OpenMP/function-filtering-2.f90 +++ b/flang/test/Lower/OpenMP/function-filtering-2.f90 @@ -26,9 +26,7 @@ subroutine no_declaretarget() end subroutine no_declaretarget ! MLIR-HOST: func.func @{{.*}}main( -! MLIR-HOST-NOT: func.func @{{.*}}main_omp_outline{{.*}}() ! MLIR-DEVICE-NOT: func.func @{{.*}}main( -! MLIR-DEVICE: func.func @{{.*}}main_omp_outline{{.*}}() attributes {omp.declare_target = #omp.declaretarget, omp.outline_parent_name = "_QQmain"} ! MLIR-ALL: return ! LLVM-HOST: define {{.*}} @{{.*}}main{{.*}}( diff --git a/flang/test/Lower/OpenMP/function-filtering.f90 b/flang/test/Lower/OpenMP/function-filtering.f90 index 3de14aa4709fc..e550348e50692 100644 --- a/flang/test/Lower/OpenMP/function-filtering.f90 +++ b/flang/test/Lower/OpenMP/function-filtering.f90 @@ -34,14 +34,9 @@ end function host_fn ! MLIR-HOST: func.func @{{.*}}target_subr( ! MLIR-HOST: return -! MLIR-HOST-NOT: func.func @{{.*}}target_subr_omp_outline_0( -! MLIR-DEVICE-NOT: func.func @{{.*}}target_subr( -! MLIR-DEVICE: func.func @{{.*}}target_subr_omp_outline_0( ! MLIR-DEVICE: return -! LLVM-ALL-NOT: define {{.*}} @{{.*}}target_subr_omp_outline_0{{.*}}( ! LLVM-HOST: define {{.*}} @{{.*}}target_subr{{.*}}( -! LLVM-DEVICE-NOT: {{.*}} @{{.*}}target_subr{{.*}}( ! LLVM-ALL: define {{.*}} @__omp_offloading_{{.*}}_{{.*}}_target_subr__{{.*}}( subroutine target_subr(x) integer, intent(out) :: x diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp index 1daf60b8659bb..422bbded2cbfc 100644 --- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp @@ -2459,6 +2459,8 @@ convertDeclareTargetAttr(Operation *op, mlir::omp::DeclareTargetAttr attribute, if (declareType == omp::DeclareTargetDeviceType::host) { llvm::Function *llvmFunc = moduleTranslation.lookupFunction(funcOp.getName()); + llvmFunc->replaceAllUsesWith( + llvm::UndefValue::get(llvmFunc->getType())); llvmFunc->dropAllReferences(); llvmFunc->eraseFromParent(); }