/// <summary> /// Returns a SMT-LIB v2.0-compliant string that corresponds to /// the input Boolean Expression. /// </summary> /// /// <param name="expr">Boolean Expression.</param> /// <returns>SMT-LIB v2.0-compliant string that corresponds to /// the input Boolean Expression.</returns> private static string MakeBooleanExpr(Expression expr) { string result = null; OperatorType exprOpType = expr.Op.Type; switch (exprOpType) { case OperatorType.And: case OperatorType.Or: Expression firstExpr = expr.GetParameter(0); Expression secondExpr = expr.GetParameter(1); Pair <string, ExpressionType> convertedFirstExpr = SmtHelper.ConvertToSmtLib2(firstExpr); Pair <string, ExpressionType> convertedSecondExpr = SmtHelper.ConvertToSmtLib2(secondExpr); result = "(" + ((exprOpType == OperatorType.And) ? "and" : "or") + " " + convertedFirstExpr.First + " " + convertedSecondExpr.First + ")"; break; case OperatorType.Not: Expression toNotExpr = expr.GetParameter(0); Pair <string, ExpressionType> convertToNotExpr = SmtHelper.ConvertToSmtLib2(toNotExpr); result = "(not " + convertToNotExpr.First + ")"; break; } Trace.Assert(result != null, "PHOENIX: Could not convert Boolean Expression: " + expr.ToString()); return(result); }
/// <summary> /// Returns a string that complies with the SMT-LIB v2.0 standard and that corresponds /// to a query that checks the feasibility of the conditions along the input Path. /// </summary> /// /// <param name="path">Path to convert.</param> /// <returns>String that complies with the SMT-LIB v2.0 standard and that corresponds to /// a query that checks the feasibility of the conditions along the input Path.</returns> public static string ConvertToSmtLib2Query(Path path) { string result = ""; /* Add the command to initialize the solver with the QF_AUFBV logic. */ result += "(set-logic QF_AUFBV)" + "\n"; /* Add declarations for the Boolean constraint variables. */ string identConstraint = path.Config.IDENT_CONSTRAINT; uint constraintNum; for (constraintNum = 0; constraintNum < path.Conditions.Count; constraintNum++) { result += "(declare-fun "; result += identConstraint + constraintNum.ToString() + " "; result += "() Bool)" + "\n"; } /* Add declarations for the variables. */ foreach (Expression varExpr in path.Variables) { result += MakeVariableBv(varExpr.Value, varExpr.BitSize) + "\n"; } /* Add declarations for the array variables. */ foreach (Expression arrayVarExpr in path.ArrayVariables) { result += MakeArrayVarBv(arrayVarExpr.Value, path.ArrayDimensions[arrayVarExpr], path) + "\n"; } /* Assert the conditions along the input Path. */ constraintNum = 0; foreach (Pair <Expression, uint> conditionPair in path.Conditions) { Pair <string, ExpressionType> convertedCondition = SmtHelper.ConvertToSmtLib2(conditionPair.First); result += "(assert (= "; result += identConstraint + constraintNum.ToString() + " "; result += convertedCondition.First + "))" + "\n"; constraintNum++; } /* Assert that the Boolean constraint variables are all true. */ result += "(assert (and"; for (constraintNum = 0; constraintNum < path.Conditions.Count; constraintNum++) { result += " " + identConstraint + constraintNum.ToString(); } result += "))" + "\n"; /* Finish the query. */ result += "(check-sat)" + "\n"; result += "(exit)" + "\n"; return(result); }
/// <summary> /// Returns a SMT-LIB v2.0-compliant string that corresponds to the sort of an array of /// bitvectors with bitvector indices, having the input dimensions. /// </summary> /// /// <param name="dimensions">Dimensions of an array of bitvectors with /// bitvector indices.</param> /// <param name="path">Path that contains the input array /// whose dimensions are provided.</param> /// <returns>SMT-LIB v2.0-compliant string that corresponds to the sort of an array of /// bitvectors with bitvector indices, having the input dimensions.</returns> private static string MakeArraySort(List <uint> dimensions, Path path) { string result = "(Array"; if (!path.ProjectConfig.MODEL_AS_NESTED_ARRAYS) { long sumDomainDims = dimensions.GetRange(0, dimensions.Count - 1).Sum(dim => dim); result += " " + SmtHelper.MakeBvSort(sumDomainDims) + " "; result += SmtHelper.MakeBvSort(dimensions[dimensions.Count - 1]) + ")"; return(result); } if (dimensions.Count == 1) { return(SmtHelper.MakeBvSort(dimensions[0])); } result += " " + SmtHelper.MakeBvSort(dimensions[0]) + " "; result += SmtHelper.MakeArraySort(dimensions.GetRange(1, dimensions.Count - 1), path); result = result.TrimEnd() + ")"; return(result); }
/// <summary> /// Converts a Boolean Expression into a logically equivalent ITE statement that returns /// a bitvector instead. If the Expression is not Boolean, it is returned unchanged. /// </summary> /// /// <param name="exprString">SMT-LIB v2.0-compliant string that represents /// the Expression to convert.</param> /// <param name="exprType">Type of the Expression.</param> /// <param name="bitSize">Bit-size of the resulting bitvector.</param> /// <returns>Logically equivalent ITE statement if the Expression is Boolean; /// unchanged input Expression otherwise.</returns> private static string BooleanToIte(string exprString, ExpressionType exprType, uint bitSize) { string result = null; switch (exprType) { case ExpressionType.DEFAULT: result = exprString; break; case ExpressionType.BOOLEAN: string zeroBv = SmtHelper.MakeConstantBv("0", bitSize); string oneBv = SmtHelper.MakeConstantBv("1", bitSize); result = "(ite " + exprString + " " + oneBv + " " + zeroBv + ")"; break; default: Trace.Fail("PHOENIX: Unrecognized Expression type: " + exprType.ToString()); break; } return(result); }
/// <summary> /// Returns a pair whose first element is a string that complies with the SMT-LIB v2.0 /// standard and that corresponds to the input Expression, and whose second element /// is the type of the Expression. /// </summary> /// /// <param name="expr">Expression to convert.</param> /// <returns>Pair whose first element is a string that complies with the SMT-LIB v2.0 /// standard and that corresponds to the input Expression, and whose second element /// is the type of the Expression.</returns> private static Pair <string, ExpressionType> ConvertToSmtLib2(Expression expr) { Pair <string, ExpressionType> result = null; string resultString = ""; OperatorType exprOpType = expr.Op.Type; switch (exprOpType) { case OperatorType.True: result = new Pair <string, ExpressionType>("true", ExpressionType.BOOLEAN); break; case OperatorType.False: result = new Pair <string, ExpressionType>("false", ExpressionType.BOOLEAN); break; case OperatorType.Constant: resultString = SmtHelper.MakeConstantBv(expr.Value, expr.BitSize); result = new Pair <string, ExpressionType>(resultString, ExpressionType.DEFAULT); break; case OperatorType.Variable: case OperatorType.ArrayVariable: result = new Pair <string, ExpressionType>(expr.Value, ExpressionType.DEFAULT); break; case OperatorType.ZeroExtend: case OperatorType.SignExtend: Expression toExtendExpr = expr.GetParameter(0); Expression extendByExpr = expr.GetParameter(1); Pair <string, ExpressionType> convertedToExtendExpr = SmtHelper.ConvertToSmtLib2(toExtendExpr); resultString = "((_ " + (exprOpType == OperatorType.ZeroExtend ? "zero_extend" : "sign_extend") + " " + extendByExpr.Value + ") "; resultString += SmtHelper.BooleanToIte(convertedToExtendExpr.First, convertedToExtendExpr.Second, toExtendExpr.BitSize) + ")"; result = new Pair <string, ExpressionType>(resultString, ExpressionType.DEFAULT); break; case OperatorType.BitExtract: Expression toExtractExpr = expr.GetParameter(0); Expression lowIndexExpr = expr.GetParameter(1); Expression highIndexExpr = expr.GetParameter(2); Pair <string, ExpressionType> convertedToExtractExpr = SmtHelper.ConvertToSmtLib2(toExtractExpr); resultString = "((_ extract " + highIndexExpr.Value + " " + lowIndexExpr.Value + ") "; resultString += SmtHelper.BooleanToIte(convertedToExtractExpr.First, convertedToExtractExpr.Second, toExtractExpr.BitSize) + ")"; result = new Pair <string, ExpressionType>(resultString, ExpressionType.DEFAULT); break; case OperatorType.Ite: Expression conditionalExpr = expr.GetParameter(0); Expression consequentExpr = expr.GetParameter(1); Expression alternateExpr = expr.GetParameter(2); Pair <string, ExpressionType> convertedConditionalExpr = SmtHelper.ConvertToSmtLib2(conditionalExpr); Pair <string, ExpressionType> convertedConsequentExpr = SmtHelper.ConvertToSmtLib2(consequentExpr); Pair <string, ExpressionType> convertedAlternateExpr = SmtHelper.ConvertToSmtLib2(alternateExpr); resultString = "(ite " + convertedConditionalExpr.First + " "; resultString += SmtHelper.BooleanToIte(convertedConsequentExpr.First, convertedConsequentExpr.Second, consequentExpr.BitSize) + " "; resultString += SmtHelper.BooleanToIte(convertedAlternateExpr.First, convertedAlternateExpr.Second, alternateExpr.BitSize) + ")"; result = new Pair <string, ExpressionType>(resultString, ExpressionType.DEFAULT); break; case OperatorType.And: case OperatorType.Or: case OperatorType.Not: resultString = SmtHelper.MakeBooleanExpr(expr); result = new Pair <string, ExpressionType>(resultString, ExpressionType.BOOLEAN); break; case OperatorType.NotEqual: Expression firstParam = expr.GetParameter(0); Expression secondParam = expr.GetParameter(1); Pair <string, ExpressionType> convertedFirstParam = SmtHelper.ConvertToSmtLib2(firstParam); Pair <string, ExpressionType> convertedSecondParam = SmtHelper.ConvertToSmtLib2(secondParam); resultString = "(not (= "; resultString += SmtHelper.BooleanToIte(convertedFirstParam.First, convertedFirstParam.Second, firstParam.BitSize) + " "; resultString += SmtHelper.BooleanToIte(convertedSecondParam.First, convertedSecondParam.Second, secondParam.BitSize) + "))"; result = new Pair <string, ExpressionType>(resultString, ExpressionType.BOOLEAN); break; default: string convertedParams = ""; foreach (Expression paramExpr in expr.ParameterList) { Pair <string, ExpressionType> convertedParam = SmtHelper.ConvertToSmtLib2(paramExpr); convertedParams += " " + SmtHelper.BooleanToIte(convertedParam.First, convertedParam.Second, paramExpr.BitSize); } OperatorType opType = expr.Op.Type; ExpressionType resultType = SmtHelper.IsComparisonOp(opType) ? ExpressionType.BOOLEAN : ExpressionType.DEFAULT; string functionForOp = ""; int numParams = expr.ParameterList.Count; switch (numParams) { case 1: functionForOp = SmtHelper.GetFunctionForUnary(opType); break; case 2: functionForOp = SmtHelper.GetFunctionForBinary(opType); break; case 3: functionForOp = SmtHelper.GetFunctionForTernary(opType); break; default: throw new NotImplementedException("PHOENIX: " + "No support for operators with arity " + numParams.ToString() + "."); } resultString = "(" + functionForOp + convertedParams + ")"; result = new Pair <string, ExpressionType>(resultString, resultType); break; } Trace.Assert(result != null, "PHOENIX: Result should not be null."); return(result); }
/// <summary> /// Returns a SMT-LIB v2.0-compliant string that declares the input array variable name /// as an array of bitvectors with bitvector indices. /// </summary> /// /// <param name="name">Array variable name.</param> /// <param name="dimensions">Dimensions of the array with the input name.</param> /// <param name="path">Path that contains the array with the input name.</param> /// <returns>SMT-LIB v2.0-compliant string that declares the input array variable name /// as an array of bitvectors with bitvector indices.</returns> private static string MakeArrayVarBv(string name, List <uint> dimensions, Path path) { return("(declare-fun " + name + " () " + SmtHelper.MakeArraySort(dimensions, path) + ")"); }
/// <summary> /// Finds the conditions along a path. /// </summary> /// /// <param name="functionUnit">Phoenix function unit.</param> /// <param name="config">Configuration object that contains GameTime /// configuration information.</param> /// <param name="projectConfig">ProjectConfiguration object that /// contains project configuration information.</param> public static void FindPathConditions(Phx.FunctionUnit functionUnit, Utilities.Configuration config, ProjectConfiguration projectConfig) { Console.Out.WriteLine("PHOENIX: Finding the conditions and " + "assignments along a path..."); Console.Out.WriteLine("PHOENIX: Reading in the DAG..."); /* Read in the DAG and deduce associated information. The DAG is in DOT file format. */ string dagFileName = System.IO.Path.Combine(projectConfig.locationTempDir, config.TEMP_DAG); TextReader dagReader = new StreamReader(dagFileName); /* Ignore the first line. */ dagReader.ReadLine(); /* Read in the nodes and the edges of the graph. */ HashSet <string> dagNodes = new HashSet <string>(); HashSet <Pair <string, string> > dagEdges = new HashSet <Pair <string, string> >(); string dagEdge = null; while ((dagEdge = dagReader.ReadLine()) != null) { dagEdge = dagEdge.Replace("{", ""); dagEdge = dagEdge.Replace("}", ""); dagEdge.Trim(); if (dagEdge.Length != 0) { /* Ignore the semicolon. */ dagEdge = dagEdge.Trim(';'); /* Split at the arrow. */ dagEdge = dagEdge.Replace("->", ">"); string[] edgeNodes = dagEdge.Split('>'); /* Find the nodes incident to the edge. */ edgeNodes[0] = edgeNodes[0].Trim(); edgeNodes[1] = edgeNodes[1].Trim(); /* Add the nodes and the edge. */ dagNodes.Add(edgeNodes[0]); dagNodes.Add(edgeNodes[1]); dagEdges.Add(new Pair <string, string>(edgeNodes[0], edgeNodes[1])); } } dagReader.Close(); uint numNodes = (uint)dagNodes.Count; uint numEdges = (uint)dagEdges.Count; Console.Out.WriteLine("PHOENIX: DAG has " + numNodes + " nodes and " + numEdges + " edges."); Console.Out.WriteLine("PHOENIX: There are at most " + (numEdges - numNodes + 2) + " basis paths."); /* How many nodes are there in the original DAG, without the fake * intermediate nodes? */ uint actualNumNodes = (numNodes / 2); uint actualNumEdges = (numEdges - actualNumNodes - 1); /* Get the successors of the nodes that existed in the * original DAG (without the fake intermediate nodes). */ Dictionary <uint, uint> nodeSuccessors = new Dictionary <uint, uint>(); foreach (Pair <string, string> edge in dagEdges) { uint sourceNodeId = Convert.ToUInt32(edge.First); uint sinkNodeId = Convert.ToUInt32(edge.Second); /* Yes, there can be more than one successor for any given node. * However, we are guaranteed that for the nodes in the original * DAG, there is exactly one (fake) successor node. We are only * concerned about these nodes. There is no great way to programatically * determine these successor nodes. */ if (sourceNodeId <= actualNumNodes) { nodeSuccessors.Add(sourceNodeId, sinkNodeId); } } Console.Out.WriteLine("PHOENIX: Finished reading and processing the DAG."); Console.Out.WriteLine("PHOENIX: Reading the block ID map..."); /* Read and store the mapping between "adjusted" block IDs and "actual" * block IDs. Also, store the reverse mapping for quick conversion * in the other direction. */ Dictionary <uint, uint> adjustedToActual = new Dictionary <uint, uint>(); Dictionary <uint, uint> actualToAdjusted = new Dictionary <uint, uint>(); string idMapFileName = System.IO.Path.Combine(projectConfig.locationTempDir, config.TEMP_DAG_ID_MAP); string[] idMappings = File.ReadAllLines(idMapFileName); foreach (string idMapping in idMappings) { string[] idMappingArray = idMapping.Split(' '); uint adjustedBlockId = Convert.ToUInt32(idMappingArray[0]); uint actualBlockId = Convert.ToUInt32(idMappingArray[1]); adjustedToActual.Add(adjustedBlockId, actualBlockId); actualToAdjusted.Add(actualBlockId, adjustedBlockId); } Console.Out.WriteLine("PHOENIX: Finished reading and processing the ID map."); Console.Out.WriteLine(); Console.Out.WriteLine("PHOENIX: Reading in the candidate path..."); /* Read in the candidate path. */ string pathNodesFileName = System.IO.Path.Combine(projectConfig.locationTempDir, config.TEMP_PATH_NODES); TextReader pathNodesReader = new StreamReader(pathNodesFileName); string[] pathNodesArray = pathNodesReader.ReadLine().Split(' '); pathNodesReader.Close(); List <uint> pathNodes = new List <uint>(); foreach (string pathNode in pathNodesArray) { if (pathNode != "") { pathNodes.Add(Convert.ToUInt32(pathNode)); } } /* Convert the adjusted block IDs in the candidate path to the actual block IDs. */ Phx.Graphs.FlowGraph flowGraph = functionUnit.FlowGraph; List <Phx.Graphs.BasicBlock> pathBlocks = new List <Phx.Graphs.BasicBlock>(); int position = 0; while (position < pathNodes.Count) { uint adjustedBlockId = pathNodes[position]; uint actualBlockId = adjustedToActual[adjustedBlockId]; Phx.Graphs.BasicBlock actualBlock = flowGraph.Node(actualBlockId) as Phx.Graphs.BasicBlock; pathBlocks.Add(actualBlock); /* Skip over the "fake" intermediate nodes. */ position = position + 2; } Path path = new Path(pathBlocks, config, projectConfig); Console.Out.WriteLine("PHOENIX: Finished reading and processing the candidate path."); /* Generate the conditions and the assignments along the path. */ Console.Out.WriteLine("PHOENIX: Generating the conditions and " + "assignments along the path..."); Console.Out.WriteLine(); path.GenerateConditionsAndAssignments(); Console.Out.WriteLine("PHOENIX: Finished generating the conditions and assignments."); if (path.ProjectConfig.debugConfig.DUMP_PATH) { path.Dump(); Console.Out.WriteLine(); } List <Pair <Expression, uint> > conditions = path.Conditions; HashSet <Expression> arrayVariables = path.ArrayVariables; Dictionary <Expression, List <uint> > arrayDimensions = path.ArrayDimensions; Console.Out.WriteLine("PHOENIX: Writing information about the path " + "to temporary files..."); Console.Out.WriteLine("PHOENIX: Writing the conditions and assignments..."); string pathConditionsFileName = System.IO.Path.Combine(projectConfig.locationTempDir, config.TEMP_PATH_CONDITIONS); StreamWriter pathConditionsWriter = new StreamWriter(pathConditionsFileName, false); pathConditionsWriter.AutoFlush = true; List <Expression> conditionExprs = new List <Expression>(); foreach (Pair <Expression, uint> conditionPair in conditions) { Expression condition = conditionPair.First; pathConditionsWriter.WriteLine(condition); conditionExprs.Add(condition); } pathConditionsWriter.Close(); Console.Out.WriteLine("PHOENIX: Writing completed."); Console.Out.WriteLine("PHOENIX: Writing line numbers..."); path.DumpLineNumbers(); Console.Out.WriteLine("PHOENIX: Writing completed."); Console.Out.WriteLine("PHOENIX: Writing the edges that correspond to " + "conditions and assignments..."); /* The actual source node that corresponds to a constraint (condition * or assignment) is the dummy node after the adjusted source node. */ path.DumpConditionEdges(sourceNodeId => nodeSuccessors[actualToAdjusted[sourceNodeId]], sinkNodeId => actualToAdjusted[sinkNodeId]); Console.Out.WriteLine("PHOENIX: Writing completed."); Console.Out.WriteLine("PHOENIX: Writing the line numbers and truth values of " + "the conditional points.."); path.DumpConditionTruths(); Console.Out.WriteLine("PHOENIX: Writing completed."); Console.Out.WriteLine("PHOENIX: Writing information about array and " + "aggregate accesses..."); path.DumpAccesses(); Console.Out.WriteLine("PHOENIX: Writing completed."); if (path.ProjectConfig.debugConfig.DUMP_ALL_PATHS) { string allPathsFileName = System.IO.Path.Combine(projectConfig.locationTempDir, config.TEMP_PATH_ALL); StreamWriter allPathsWriter = new StreamWriter(allPathsFileName, true); allPathsWriter.AutoFlush = true; allPathsWriter.Write("*** CANDIDATE PATH ***"); allPathsWriter.WriteLine(path.ToString()); allPathsWriter.Close(); } Console.Out.WriteLine("PHOENIX: Path information written to temporary files."); Console.Out.WriteLine("PHOENIX: Writing the corresponding SMT query to " + "a temporary file..."); string smtQueryFileName = System.IO.Path.Combine(projectConfig.locationTempDir, config.TEMP_PATH_QUERY + ".smt"); StreamWriter smtQueryWriter = new StreamWriter(smtQueryFileName, false); smtQueryWriter.AutoFlush = true; smtQueryWriter.Write(SmtHelper.ConvertToSmtLib2Query(path)); smtQueryWriter.Close(); Console.Out.WriteLine("PHOENIX: Writing completed."); }