@@ -115,9 +115,15 @@ struct RDVFinalCleanupList {
115115
116116// / Return true iff at least one value in `values` is live, given the liveness
117117// / information in `la`.
118- static bool hasLive (ValueRange values, const DenseSet<Value> &nonLiveSet,
118+ static bool hasLive (ValueRange values, const DenseSet<Value> &nonLiveSet, const DenseSet<Value> &liveSet,
119+
119120 RunLivenessAnalysis &la) {
120121 for (Value value : values) {
122+ if (liveSet.contains (value)) {
123+ LDBG () << " Value " << value << " is marked live by CallOp" ;
124+ return true ;
125+ }
126+
121127 if (nonLiveSet.contains (value)) {
122128 LDBG () << " Value " << value << " is already marked non-live (dead)" ;
123129 continue ;
@@ -257,8 +263,9 @@ static SmallVector<OpOperand *> operandsToOpOperands(OperandRange operands) {
257263// / - Return-like
258264static void processSimpleOp (Operation *op, RunLivenessAnalysis &la,
259265 DenseSet<Value> &nonLiveSet,
266+ DenseSet<Value> &liveSet,
260267 RDVFinalCleanupList &cl) {
261- if (!isMemoryEffectFree (op) || hasLive (op->getResults (), nonLiveSet, la)) {
268+ if (!isMemoryEffectFree (op) || hasLive (op->getResults (), nonLiveSet, liveSet, la)) {
262269 LDBG () << " Simple op is not memory effect free or has live results, "
263270 " preserving it: "
264271 << OpWithFlags (op, OpPrintingFlags ().skipRegions ());
@@ -376,6 +383,31 @@ static void processFuncOp(FunctionOpInterface funcOp, Operation *module,
376383 }
377384}
378385
386+ static void processCallOp (CallOpInterface callOp, Operation *module ,
387+ RunLivenessAnalysis &la,
388+ DenseSet<Value> &liveSet) {
389+ auto callable = callOp.getCallableForCallee ();
390+
391+ if (auto symbolRef = callable.dyn_cast <SymbolRefAttr>()) {
392+ Operation *calleeOp = SymbolTable::lookupSymbolIn (module , symbolRef);
393+
394+ if (auto funcOp = llvm::dyn_cast_or_null<mlir::FunctionOpInterface>(calleeOp)) {
395+ // Ensure the outgoing arguments of PUBLIC functions are live
396+ // because processFuncOp can not process them.
397+ //
398+ // Liveness treats the external function as a blackbox.
399+ if (funcOp.isPublic ()) {
400+ for (Value arg: callOp.getArgOperands ()) {
401+ const Liveness *liveness = la.getLiveness (arg);
402+ if (liveness && !liveness->isLive ) {
403+ liveSet.insert (arg);
404+ }
405+ }
406+ }
407+ }
408+ }
409+ }
410+
379411// / Process a region branch operation `regionBranchOp` using the liveness
380412// / information in `la`. The processing involves two scenarios:
381413// /
@@ -408,6 +440,7 @@ static void processFuncOp(FunctionOpInterface funcOp, Operation *module,
408440static void processRegionBranchOp (RegionBranchOpInterface regionBranchOp,
409441 RunLivenessAnalysis &la,
410442 DenseSet<Value> &nonLiveSet,
443+ DenseSet<Value> &liveSet,
411444 RDVFinalCleanupList &cl) {
412445 LDBG () << " Processing region branch op: "
413446 << OpWithFlags (regionBranchOp, OpPrintingFlags ().skipRegions ());
@@ -616,7 +649,7 @@ static void processRegionBranchOp(RegionBranchOpInterface regionBranchOp,
616649 // attributed to something else.
617650 // Do (1') and (2').
618651 if (isMemoryEffectFree (regionBranchOp.getOperation ()) &&
619- !hasLive (regionBranchOp->getResults (), nonLiveSet, la)) {
652+ !hasLive (regionBranchOp->getResults (), nonLiveSet, liveSet, la)) {
620653 cl.operations .push_back (regionBranchOp.getOperation ());
621654 return ;
622655 }
@@ -834,16 +867,18 @@ void RemoveDeadValues::runOnOperation() {
834867 // Tracks values eligible for erasure - complements liveness analysis to
835868 // identify "droppable" values.
836869 DenseSet<Value> deadVals;
870+ // mark outgoing arguments to a public function LIVE.
871+ DenseSet<Value> liveVals;
837872
838873 // Maintains a list of Ops, values, branches, etc., slated for cleanup at the
839874 // end of this pass.
840875 RDVFinalCleanupList finalCleanupList;
841876
842- module ->walk ([&](Operation *op) {
877+ module ->walk <WalkOrder::PostOrder, BackwardIterator> ([&](Operation *op) {
843878 if (auto funcOp = dyn_cast<FunctionOpInterface>(op)) {
844879 processFuncOp (funcOp, module , la, deadVals, finalCleanupList);
845880 } else if (auto regionBranchOp = dyn_cast<RegionBranchOpInterface>(op)) {
846- processRegionBranchOp (regionBranchOp, la, deadVals, finalCleanupList);
881+ processRegionBranchOp (regionBranchOp, la, deadVals, liveVals, finalCleanupList);
847882 } else if (auto branchOp = dyn_cast<BranchOpInterface>(op)) {
848883 processBranchOp (branchOp, la, deadVals, finalCleanupList);
849884 } else if (op->hasTrait <::mlir::OpTrait::IsTerminator>()) {
@@ -852,8 +887,13 @@ void RemoveDeadValues::runOnOperation() {
852887 } else if (isa<CallOpInterface>(op)) {
853888 // Nothing to do because this op is associated with a function op and gets
854889 // cleaned when the latter is cleaned.
890+ //
891+ // The only exception is public callee. By default, Liveness analysis is inter-procedural.
892+ // Unused arguments of a public function nonLive and are propagated to the caller.
893+ // processCallOp puts them to liveVals.
894+ processCallOp (cast<CallOpInterface>(op), module , la, liveVals);
855895 } else {
856- processSimpleOp (op, la, deadVals, finalCleanupList);
896+ processSimpleOp (op, la, deadVals, liveVals, finalCleanupList);
857897 }
858898 });
859899
0 commit comments