You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@systemml.apache.org by mb...@apache.org on 2018/08/14 08:12:10 UTC

systemml git commit: [SYSTEMML-2488] Fix function inlining of multi-return builtin functions

Repository: systemml
Updated Branches:
  refs/heads/master 1e851ef62 -> db0207900


[SYSTEMML-2488] Fix function inlining of multi-return builtin functions

The inlining during parser validate of functions that call multi-return
builtin functions is invalid in special cases as the append of output
mapping statements can violate internal assumptions. This patch
introduces new mechanics for doing in-place function output mapping.
Since the compiler still struggles to handle these multi-return builtin
operations, this also includes a correctness fix for deciding on
function inlining.


Project: http://git-wip-us.apache.org/repos/asf/systemml/repo
Commit: http://git-wip-us.apache.org/repos/asf/systemml/commit/db020790
Tree: http://git-wip-us.apache.org/repos/asf/systemml/tree/db020790
Diff: http://git-wip-us.apache.org/repos/asf/systemml/diff/db020790

Branch: refs/heads/master
Commit: db02079000e1cba56c938374b1fa1641d40b551d
Parents: 1e851ef
Author: Matthias Boehm <mb...@gmail.com>
Authored: Tue Aug 14 01:10:09 2018 -0700
Committer: Matthias Boehm <mb...@gmail.com>
Committed: Tue Aug 14 01:14:01 2018 -0700

----------------------------------------------------------------------
 .../sysml/parser/AssignmentStatement.java       |   4 +
 .../sysml/parser/MultiAssignmentStatement.java  |   6 +
 .../org/apache/sysml/parser/StatementBlock.java | 295 +++++++++++--------
 .../functions/misc/FunctionPotpourriTest.java   |  21 +-
 .../misc/FunPotpourriMultiReturnBuiltin1.dml    |  28 ++
 .../misc/FunPotpourriMultiReturnBuiltin2.dml    |  34 +++
 6 files changed, 260 insertions(+), 128 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/systemml/blob/db020790/src/main/java/org/apache/sysml/parser/AssignmentStatement.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/sysml/parser/AssignmentStatement.java b/src/main/java/org/apache/sysml/parser/AssignmentStatement.java
index 72b2ce8..1a4963c 100644
--- a/src/main/java/org/apache/sysml/parser/AssignmentStatement.java
+++ b/src/main/java/org/apache/sysml/parser/AssignmentStatement.java
@@ -73,6 +73,10 @@ public class AssignmentStatement extends Statement
 		return _targetList;
 	}
 
+	public void setTarget(DataIdentifier di) {
+		_targetList.set(0, di);
+	}
+	
 	public Expression getSource(){
 		return _source;
 	}

http://git-wip-us.apache.org/repos/asf/systemml/blob/db020790/src/main/java/org/apache/sysml/parser/MultiAssignmentStatement.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/sysml/parser/MultiAssignmentStatement.java b/src/main/java/org/apache/sysml/parser/MultiAssignmentStatement.java
index b01dd2e..7909f71 100644
--- a/src/main/java/org/apache/sysml/parser/MultiAssignmentStatement.java
+++ b/src/main/java/org/apache/sysml/parser/MultiAssignmentStatement.java
@@ -20,6 +20,7 @@
 package org.apache.sysml.parser;
 
 import java.util.ArrayList;
+import java.util.List;
 
 import org.apache.sysml.api.DMLScript;
 import org.apache.sysml.debug.DMLBreakpointManager;
@@ -61,6 +62,11 @@ public class MultiAssignmentStatement extends Statement
 		return _targetList;
 	}
 	
+	public void setTargetList(List<DataIdentifier> diList) {
+		_targetList.clear();
+		_targetList.addAll(diList);
+	}
+	
 	public Expression getSource(){
 		return _source;
 	}

http://git-wip-us.apache.org/repos/asf/systemml/blob/db020790/src/main/java/org/apache/sysml/parser/StatementBlock.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/sysml/parser/StatementBlock.java b/src/main/java/org/apache/sysml/parser/StatementBlock.java
index 4498eaa..3988a7f 100644
--- a/src/main/java/org/apache/sysml/parser/StatementBlock.java
+++ b/src/main/java/org/apache/sysml/parser/StatementBlock.java
@@ -288,12 +288,18 @@ public class StatementBlock extends LiveVariableAnalysis implements ParseInfo
 
     				if( !ret ) return false;
         		}
-        		if( s instanceof MultiAssignmentStatement && ((MultiAssignmentStatement)s).getSource() instanceof FunctionCallIdentifier )
-        		{
-        			FunctionCallIdentifier fcall = (FunctionCallIdentifier) ((MultiAssignmentStatement)s).getSource();
-    				FunctionStatementBlock fblock2 = prog.getFunctionStatementBlock(fcall.getNamespace(), fcall.getName());
-    				ret &= rIsInlineableFunction(fblock2, prog);
-    				if( !ret ) return false;
+        		else if( s instanceof MultiAssignmentStatement ) {
+        			MultiAssignmentStatement mas = (MultiAssignmentStatement)s;
+        			if( mas.getSource() instanceof FunctionCallIdentifier ) {
+        				FunctionCallIdentifier fcall = (FunctionCallIdentifier) ((MultiAssignmentStatement)s).getSource();
+        				FunctionStatementBlock fblock2 = prog.getFunctionStatementBlock(fcall.getNamespace(), fcall.getName());
+        				ret &= rIsInlineableFunction(fblock2, prog);
+        				if( !ret ) return false;
+        			}
+        			else if( mas.getSource() instanceof BuiltinFunctionExpression
+        				&& ((BuiltinFunctionExpression)mas.getSource()).multipleReturns() ) {
+        				return false;
+        			}
         		}
         	}
 		}
@@ -574,143 +580,180 @@ public class StatementBlock extends LiveVariableAnalysis implements ParseInfo
 	public ArrayList<Statement> rewriteFunctionCallStatements (DMLProgram dmlProg, ArrayList<Statement> statements) {
 
 		ArrayList<Statement> newStatements = new ArrayList<>();
-		for (Statement current : statements){
-			if (isRewritableFunctionCall(current, dmlProg)){
-
-				Expression sourceExpr = null;
-				if (current instanceof AssignmentStatement)
-					sourceExpr = ((AssignmentStatement)current).getSource();
-				else
-					sourceExpr = ((MultiAssignmentStatement)current).getSource();
+		for (Statement current : statements) {
+			if( !isRewritableFunctionCall(current, dmlProg) ) {
+				newStatements.add(current);
+				continue;
+			}
 
-				FunctionCallIdentifier fcall = (FunctionCallIdentifier) sourceExpr;
-				FunctionStatementBlock fblock = dmlProg.getFunctionStatementBlock(fcall.getNamespace(), fcall.getName());
-				if (fblock == null){
-					fcall.raiseValidateError("function " + fcall.getName() + " is undefined in namespace " + fcall.getNamespace(), false);
-				}
-				FunctionStatement fstmt = (FunctionStatement)fblock.getStatement(0);
+			Expression sourceExpr = (current instanceof AssignmentStatement) ?
+				((AssignmentStatement)current).getSource() :
+				((MultiAssignmentStatement)current).getSource();
+			FunctionCallIdentifier fcall = (FunctionCallIdentifier) sourceExpr;
+			FunctionStatementBlock fblock = dmlProg.getFunctionStatementBlock(fcall.getNamespace(), fcall.getName());
+			if( fblock == null )
+				fcall.raiseValidateError("function " + fcall.getName() + " is undefined in namespace " + fcall.getNamespace(), false);
+			FunctionStatement fstmt = (FunctionStatement)fblock.getStatement(0);
+
+			// recursive inlining (no memo required because update-inplace of function statement blocks, so no redundant inlining)
+			if( rIsInlineableFunction(fblock, dmlProg) ){
+				fstmt.getBody().get(0).setStatements(
+					rewriteFunctionCallStatements(dmlProg, fstmt.getBody().get(0).getStatements()));
+			}
 
-				// recursive inlining (no memo required because update-inplace of function statement blocks, so no redundant inlining)
-				if( rIsInlineableFunction(fblock, dmlProg) ){
-					fstmt.getBody().get(0).setStatements(rewriteFunctionCallStatements(dmlProg, fstmt.getBody().get(0).getStatements()));
-				}
+			//MB: we cannot use the hash since multiple interleaved inlined functions should be independent.
+			String prefix = _seq.getNextID() + "_";
 
-				//MB: we cannot use the hash since multiple interleaved inlined functions should be independent.
-				String prefix = _seq.getNextID() + "_";
+			if (fstmt.getBody().size() > 1){
+				sourceExpr.raiseValidateError("rewritable function can only have 1 statement block", false);
+			}
+			StatementBlock sblock = fstmt.getBody().get(0);
 
-				if (fstmt.getBody().size() > 1){
-					sourceExpr.raiseValidateError("rewritable function can only have 1 statement block", false);
-				}
-				StatementBlock sblock = fstmt.getBody().get(0);
+			if( fcall.getParamExprs().size() != fstmt.getInputParams().size() ) {
+				sourceExpr.raiseValidateError("Wrong number of function input arguments: "+
+					fcall.getParamExprs().size() + " found, but " + fstmt.getInputParams().size()+" expected.");
+			}
 
-				if( fcall.getParamExprs().size() != fstmt.getInputParams().size() ) {
-					sourceExpr.raiseValidateError("Wrong number of function input arguments: "+
-						fcall.getParamExprs().size() + " found, but " + fstmt.getInputParams().size()+" expected.");
+			for (int i =0; i < fcall.getParamExprs().size(); i++) {
+				ParameterExpression inputArg = fcall.getParamExprs().get(i);
+				DataIdentifier currFormalParam = (inputArg.getName()==null) ?
+					fstmt.getInputParams().get(i) : fstmt.getInputParam(inputArg.getName());
+				if( currFormalParam == null )
+					throw new LanguageException("Non-existing named function argument '"
+						+ inputArg.getName()+"' in call to "+fcall.getName()+".");
+				
+				// create new assignment statement
+				String newFormalParameterName = prefix + currFormalParam.getName();
+				DataIdentifier newTarget = new DataIdentifier(currFormalParam);
+				newTarget.setName(newFormalParameterName);
+
+				Expression currCallParam = inputArg.getExpr();
+
+				//auto casting of inputs on inlining (if required)
+				ValueType targetVT = newTarget.getValueType();
+				if (newTarget.getDataType() == DataType.SCALAR && currCallParam.getOutput() != null
+						&& targetVT != currCallParam.getOutput().getValueType() && targetVT != ValueType.STRING) {
+					currCallParam = new BuiltinFunctionExpression(
+						BuiltinFunctionExpression.getValueTypeCastOperator(targetVT),
+						new Expression[] { currCallParam }, newTarget);
 				}
 
-				for (int i =0; i < fcall.getParamExprs().size(); i++) {
-					ParameterExpression inputArg = fcall.getParamExprs().get(i);
-					DataIdentifier currFormalParam = (inputArg.getName()==null) ?
-						fstmt.getInputParams().get(i) : fstmt.getInputParam(inputArg.getName());
-					if( currFormalParam == null )
-						throw new LanguageException("Non-existing named function argument '"
-							+ inputArg.getName()+"' in call to "+fcall.getName()+".");
-					
-					// create new assignment statement
-					String newFormalParameterName = prefix + currFormalParam.getName();
-					DataIdentifier newTarget = new DataIdentifier(currFormalParam);
-					newTarget.setName(newFormalParameterName);
-
-					Expression currCallParam = inputArg.getExpr();
-
-					//auto casting of inputs on inlining (if required)
-					ValueType targetVT = newTarget.getValueType();
-					if (newTarget.getDataType() == DataType.SCALAR && currCallParam.getOutput() != null
-							&& targetVT != currCallParam.getOutput().getValueType() && targetVT != ValueType.STRING) {
-						currCallParam = new BuiltinFunctionExpression(
-							BuiltinFunctionExpression.getValueTypeCastOperator(targetVT),
-							new Expression[] { currCallParam }, newTarget);
-					}
-
-					// create the assignment statement to bind the call parameter to formal parameter
-					newStatements.add(new AssignmentStatement(newTarget, currCallParam, newTarget));
-				}
+				// create the assignment statement to bind the call parameter to formal parameter
+				newStatements.add(new AssignmentStatement(newTarget, currCallParam, newTarget));
+			}
 
-				for (Statement stmt : sblock._statements){
-					// rewrite the statement to use the "rewritten" name
-					Statement rewrittenStmt = stmt.rewriteStatement(prefix);
-					newStatements.add(rewrittenStmt);
-				}
+			for (Statement stmt : sblock._statements){
+				// rewrite the statement to use the "rewritten" name
+				Statement rewrittenStmt = stmt.rewriteStatement(prefix);
+				newStatements.add(rewrittenStmt);
+			}
 
-				if (current instanceof AssignmentStatement) {
-					if (fstmt.getOutputParams().size() == 0) {
-						AssignmentStatement as = (AssignmentStatement) current;
-						if ((as.getTargetList().size() == 1) && (as.getTargetList().get(0) != null)) {
-							raiseValidateError("Function '" + fcall.getName()
-									+ "' does not return a value but is assigned to " + as.getTargetList().get(0),
-									true);
-						}
-					}
-				} else if (current instanceof MultiAssignmentStatement) {
-					if (fstmt.getOutputParams().size() == 0) {
-						MultiAssignmentStatement mas = (MultiAssignmentStatement) current;
+			if (current instanceof AssignmentStatement) {
+				if (fstmt.getOutputParams().size() == 0) {
+					AssignmentStatement as = (AssignmentStatement) current;
+					if ((as.getTargetList().size() == 1) && (as.getTargetList().get(0) != null)) {
 						raiseValidateError("Function '" + fcall.getName()
-								+ "' does not return a value but is assigned to " + mas.getTargetList(), true);
+								+ "' does not return a value but is assigned to " + as.getTargetList().get(0),
+								true);
 					}
 				}
-				// handle the return values
-				for (int i = 0; i < fstmt.getOutputParams().size(); i++){
-
-					// get the target (return parameter from function)
-					DataIdentifier currReturnParam = fstmt.getOutputParams().get(i);
-					String newSourceName = prefix + currReturnParam.getName();
-					DataIdentifier newSource = new DataIdentifier(currReturnParam);
-					newSource.setName(newSourceName);
-
-					// get binding
-					DataIdentifier newTarget = null;
-					if (current instanceof AssignmentStatement){
-						if (i > 0) {
-							fstmt.raiseValidateError("Assignment statement cannot return multiple values", false);
-						}
-						AssignmentStatement as = (AssignmentStatement) current;
-						DataIdentifier targ = as.getTarget();
-						if (targ == null) {
-							Expression exp = as.getSource();
-							FunctionCallIdentifier fci = (FunctionCallIdentifier) exp;
-							String functionName = fci.getName();
-							fstmt.raiseValidateError(functionName + " requires LHS value", false);
-						} else {
-							newTarget = new DataIdentifier(((AssignmentStatement)current).getTarget());
-						}
-					}
-					else{
-						newTarget = new DataIdentifier(((MultiAssignmentStatement)current).getTargetList().get(i));
-					}
-
-					//auto casting of inputs on inlining (always, redundant cast removed during Hop Rewrites)
-					ValueType sourceVT = newSource.getValueType();
-					if (newSource.getDataType() == DataType.SCALAR && sourceVT != ValueType.STRING) {
-						newSource = new BuiltinFunctionExpression(
-								BuiltinFunctionExpression.getValueTypeCastOperator(sourceVT),
-								new Expression[] { newSource }, newTarget);
-					}
-
-					// create the assignment statement to bind the call parameter to formal parameter
-					AssignmentStatement binding = new AssignmentStatement(newTarget, newSource, newTarget);
-
-					newStatements.add(binding);
+			} else if (current instanceof MultiAssignmentStatement) {
+				if (fstmt.getOutputParams().size() == 0) {
+					MultiAssignmentStatement mas = (MultiAssignmentStatement) current;
+					raiseValidateError("Function '" + fcall.getName()
+							+ "' does not return a value but is assigned to " + mas.getTargetList(), true);
 				}
-
-			} // end if (isRewritableFunctionCall(current, dmlProg)
-
-			else {
-				newStatements.add(current);
 			}
+			
+			// handle returns by appending name mappings, but with special handling of 
+			// statements that contain function calls or multi-return builtin expressions (but disabled)
+//			Statement lastAdd = newStatements.get(newStatements.size()-1);
+//			if( isOutputBindingViaFunctionCall(lastAdd, prefix, fstmt) && lastAdd instanceof AssignmentStatement )
+//				((AssignmentStatement)lastAdd).setTarget(((AssignmentStatement)current).getTarget());
+//			else if ( isOutputBindingViaFunctionCall(lastAdd, prefix, fstmt) && lastAdd instanceof MultiAssignmentStatement )
+//				if( current instanceof MultiAssignmentStatement )
+//					((MultiAssignmentStatement)lastAdd).setTargetList(((MultiAssignmentStatement)current).getTargetList());
+//				else //correct for multi-assignment to assignment transform
+//					newStatements.set(newStatements.size()-1, createNewPartialMultiAssignment(lastAdd, current, prefix, fstmt));
+//			else
+			appendOutputAssignments(current, prefix, fstmt, newStatements);
 		}
-
 		return newStatements;
 	}
+	
+	@SuppressWarnings("unused")
+	private static boolean isOutputBindingViaFunctionCall(Statement last, String prefix, FunctionStatement fstmt) {
+		if( last instanceof AssignmentStatement ) {
+			AssignmentStatement as = (AssignmentStatement) last;
+			String newName = prefix + fstmt.getOutputParams().get(0).getName();
+			return as.getSource() instanceof FunctionCallIdentifier
+				&& as.getTarget().getName().equals(newName);
+		}
+		else if( last instanceof MultiAssignmentStatement ) {
+			MultiAssignmentStatement mas = (MultiAssignmentStatement) last;
+			List<DataIdentifier> tlist1 = mas.getTargetList();
+			boolean ret = mas.getSource() instanceof FunctionCallIdentifier
+				|| (mas.getSource() instanceof BuiltinFunctionExpression 
+					&& ((BuiltinFunctionExpression)mas.getSource()).multipleReturns());
+			for( DataIdentifier di : fstmt.getOutputParams() )
+				ret &= tlist1.stream().anyMatch(d -> d.getName().equals(prefix+di.getName()));
+			return ret;
+		}
+		return false; //default
+	}
+	
+	@SuppressWarnings("unused")
+	private static MultiAssignmentStatement createNewPartialMultiAssignment(Statement last, Statement current, String prefix, FunctionStatement fstmt) {
+		MultiAssignmentStatement mas = (MultiAssignmentStatement) last;
+		AssignmentStatement as = (AssignmentStatement) current;
+		ArrayList<DataIdentifier> tlist = new ArrayList<>();
+		String tmpStr = prefix+fstmt.getOutputParams().get(0).getName();
+		for( DataIdentifier di : mas.getTargetList() )
+			tlist.add( di.getName().equals(tmpStr) ? as.getTarget() : di );
+		return new MultiAssignmentStatement(tlist, mas.getSource());
+	}
+	
+	private static void appendOutputAssignments(Statement current, String prefix, FunctionStatement fstmt, List<Statement> newStatements) {
+		for (int i = 0; i < fstmt.getOutputParams().size(); i++){
+			// get the target (return parameter from function)
+			DataIdentifier currReturnParam = fstmt.getOutputParams().get(i);
+			String newSourceName = prefix + currReturnParam.getName();
+			DataIdentifier newSource = new DataIdentifier(currReturnParam);
+			newSource.setName(newSourceName);
+
+			// get binding
+			DataIdentifier newTarget = null;
+			if (current instanceof AssignmentStatement){
+				if (i > 0) {
+					fstmt.raiseValidateError("Assignment statement cannot return multiple values", false);
+				}
+				AssignmentStatement as = (AssignmentStatement) current;
+				DataIdentifier targ = as.getTarget();
+				if (targ == null) {
+					Expression exp = as.getSource();
+					FunctionCallIdentifier fci = (FunctionCallIdentifier) exp;
+					String functionName = fci.getName();
+					fstmt.raiseValidateError(functionName + " requires LHS value", false);
+				} else {
+					newTarget = new DataIdentifier(((AssignmentStatement)current).getTarget());
+				}
+			}
+			else{
+				newTarget = new DataIdentifier(((MultiAssignmentStatement)current).getTargetList().get(i));
+			}
+
+			//auto casting of inputs on inlining (always, redundant cast removed during Hop Rewrites)
+			ValueType sourceVT = newSource.getValueType();
+			if (newSource.getDataType() == DataType.SCALAR && sourceVT != ValueType.STRING) {
+				newSource = new BuiltinFunctionExpression(
+					BuiltinFunctionExpression.getValueTypeCastOperator(sourceVT),
+					new Expression[] { newSource }, newTarget);
+			}
+
+			// create the assignment statement to bind the call parameter to formal parameter
+			newStatements.add(new AssignmentStatement(newTarget, newSource, newTarget));
+		}
+	}
 
 	public VariableSet validate(DMLProgram dmlProg, VariableSet ids, HashMap<String, ConstIdentifier> constVars, boolean conditional)
 	{

http://git-wip-us.apache.org/repos/asf/systemml/blob/db020790/src/test/java/org/apache/sysml/test/integration/functions/misc/FunctionPotpourriTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/sysml/test/integration/functions/misc/FunctionPotpourriTest.java b/src/test/java/org/apache/sysml/test/integration/functions/misc/FunctionPotpourriTest.java
index 68e8f00..f1ce7b0 100644
--- a/src/test/java/org/apache/sysml/test/integration/functions/misc/FunctionPotpourriTest.java
+++ b/src/test/java/org/apache/sysml/test/integration/functions/misc/FunctionPotpourriTest.java
@@ -20,6 +20,7 @@
 package org.apache.sysml.test.integration.functions.misc;
 
 
+import org.junit.Assert;
 import org.junit.Test;
 import org.apache.sysml.api.DMLException;
 import org.apache.sysml.test.integration.AutomatedTestBase;
@@ -45,6 +46,8 @@ public class FunctionPotpourriTest extends AutomatedTestBase
 	private final static String TEST_NAME15 = "FunPotpourriDefaultArgScalarMatrix1";
 	private final static String TEST_NAME16 = "FunPotpourriDefaultArgScalarMatrix2";
 	private final static String TEST_NAME17 = "FunPotpourriNamedArgsQuotedAssign";
+	private final static String TEST_NAME18 = "FunPotpourriMultiReturnBuiltin1";
+	private final static String TEST_NAME19 = "FunPotpourriMultiReturnBuiltin2";
 	
 	private final static String TEST_DIR = "functions/misc/";
 	private final static String TEST_CLASS_DIR = TEST_DIR + FunctionPotpourriTest.class.getSimpleName() + "/";
@@ -69,6 +72,8 @@ public class FunctionPotpourriTest extends AutomatedTestBase
 		addTestConfiguration( TEST_NAME15, new TestConfiguration(TEST_CLASS_DIR, TEST_NAME15, new String[] { "R" }) );
 		addTestConfiguration( TEST_NAME16, new TestConfiguration(TEST_CLASS_DIR, TEST_NAME16, new String[] { "R" }) );
 		addTestConfiguration( TEST_NAME17, new TestConfiguration(TEST_CLASS_DIR, TEST_NAME17, new String[] { "R" }) );
+		addTestConfiguration( TEST_NAME18, new TestConfiguration(TEST_CLASS_DIR, TEST_NAME18, new String[] { "R" }) );
+		addTestConfiguration( TEST_NAME19, new TestConfiguration(TEST_CLASS_DIR, TEST_NAME19, new String[] { "R" }) );
 	}
 
 	@Test
@@ -166,16 +171,28 @@ public class FunctionPotpourriTest extends AutomatedTestBase
 		runFunctionTest( TEST_NAME17, false );
 	}
 	
+	@Test
+	public void testFunctionMultiReturnBuiltin1() {
+		runFunctionTest( TEST_NAME18, false );
+	}
+	
+	@Test
+	public void testFunctionMultiReturnBuiltin2() {
+		runFunctionTest( TEST_NAME19, false );
+	}
+	
 	private void runFunctionTest(String testName, boolean error) {
 		TestConfiguration config = getTestConfiguration(testName);
 		loadTestConfiguration(config);
 		
 		String HOME = SCRIPT_DIR + TEST_DIR;
 		fullDMLScriptName = HOME + testName + ".dml";
-		programArgs = new String[]{"-explain", "-stats",
+		programArgs = new String[]{"-explain","hops", "-stats",
 			"-args", String.valueOf(error).toUpperCase()};
-
+		
 		//run script and compare output
 		runTest(true, error, DMLException.class, -1);
+		if( testName.equals(TEST_NAME18) )
+			Assert.assertTrue(heavyHittersContainsString("print"));
 	}
 }

http://git-wip-us.apache.org/repos/asf/systemml/blob/db020790/src/test/scripts/functions/misc/FunPotpourriMultiReturnBuiltin1.dml
----------------------------------------------------------------------
diff --git a/src/test/scripts/functions/misc/FunPotpourriMultiReturnBuiltin1.dml b/src/test/scripts/functions/misc/FunPotpourriMultiReturnBuiltin1.dml
new file mode 100644
index 0000000..cac2ac4
--- /dev/null
+++ b/src/test/scripts/functions/misc/FunPotpourriMultiReturnBuiltin1.dml
@@ -0,0 +1,28 @@
+#-------------------------------------------------------------
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+#-------------------------------------------------------------
+
+foo = function(Matrix[Double] A) return (Matrix[Double] B) {
+  [w, B] = eigen(A)
+}
+
+X = matrix(0.1, rows=100, cols=100);
+Y = foo(X);
+print(toString(Y))

http://git-wip-us.apache.org/repos/asf/systemml/blob/db020790/src/test/scripts/functions/misc/FunPotpourriMultiReturnBuiltin2.dml
----------------------------------------------------------------------
diff --git a/src/test/scripts/functions/misc/FunPotpourriMultiReturnBuiltin2.dml b/src/test/scripts/functions/misc/FunPotpourriMultiReturnBuiltin2.dml
new file mode 100644
index 0000000..1efcab4
--- /dev/null
+++ b/src/test/scripts/functions/misc/FunPotpourriMultiReturnBuiltin2.dml
@@ -0,0 +1,34 @@
+#-------------------------------------------------------------
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+#-------------------------------------------------------------
+
+foo2 = function(Matrix[Double] A) return (Matrix[Double] w, Matrix[Double] B) {
+  while(FALSE){}
+  w = A - 1;
+  B = A + 1;
+}
+
+foo = function(Matrix[Double] A) return (Matrix[Double] B) {
+  [w, B] = foo2(A)
+}
+
+X = matrix(0.1, rows=100, cols=100);
+Y = foo(X);
+print(toString(Y))