diff --git a/Ghidra/Features/Decompiler/certification.manifest b/Ghidra/Features/Decompiler/certification.manifest
index a9d18144a8..6e4bb55ba1 100644
--- a/Ghidra/Features/Decompiler/certification.manifest
+++ b/Ghidra/Features/Decompiler/certification.manifest
@@ -37,6 +37,7 @@ src/decompile/datatests/modulo.xml||GHIDRA||||END|
src/decompile/datatests/modulo2.xml||GHIDRA||||END|
src/decompile/datatests/multiret.xml||GHIDRA||||END|
src/decompile/datatests/namespace.xml||GHIDRA||||END|
+src/decompile/datatests/nan.xml||GHIDRA||||END|
src/decompile/datatests/nestedoffset.xml||GHIDRA||||END|
src/decompile/datatests/noforloop_alias.xml||GHIDRA||||END|
src/decompile/datatests/noforloop_globcall.xml||GHIDRA||||END|
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc
index 62508298f6..f03793f014 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc
@@ -1401,6 +1401,8 @@ void Architecture::resetDefaultsInternal(void)
infer_pointers = true;
analyze_for_loops = true;
readonlypropagate = false;
+ nan_ignore_all = false;
+ nan_ignore_compare = true; // Ignore only NaN operations associated with floating-point comparisons by default
alias_block_level = 2; // Block structs and arrays by default, but not more primitive data-types
split_datatype_config = OptionSplitDatatypes::option_struct | OptionSplitDatatypes::option_array
| OptionSplitDatatypes::option_pointer;
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh
index c2c4d8e3c0..98c5d705c4 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh
@@ -177,6 +177,8 @@ public:
bool readonlypropagate; ///< true if readonly values should be treated as constants
bool infer_pointers; ///< True if we should infer pointers from constants that are likely addresses
bool analyze_for_loops; ///< True if we should attempt conversion of \e whiledo loops to \e for loops
+ bool nan_ignore_all; ///< True if we should ignore NaN operations, i.e. nan() always returns false
+ bool nan_ignore_compare; ///< True if we should ignore NaN operations protecting floating-point comparisons
vector inferPtrSpaces; ///< Set of address spaces in which a pointer constant is inferable
int4 funcptr_align; ///< How many bits of alignment a function ptr has
uint4 flowoptions; ///< options passed to flow following engine
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.cc
index 9979b81400..e79d76af4f 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.cc
@@ -1160,6 +1160,6 @@ ElementId ELEM_VAL = ElementId("val",8);
ElementId ELEM_VALUE = ElementId("value",9);
ElementId ELEM_VOID = ElementId("void",10);
-ElementId ELEM_UNKNOWN = ElementId("XMLunknown",272); // Number serves as next open index
+ElementId ELEM_UNKNOWN = ElementId("XMLunknown",273); // Number serves as next open index
} // End namespace ghidra
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/options.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/options.cc
index a496b4f8e0..c193e2d515 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/options.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/options.cc
@@ -59,6 +59,7 @@ ElementId ELEM_STRUCTALIGN = ElementId("structalign",208);
ElementId ELEM_TOGGLERULE = ElementId("togglerule",209);
ElementId ELEM_WARNING = ElementId("warning",210);
ElementId ELEM_JUMPTABLEMAX = ElementId("jumptablemax",271);
+ElementId ELEM_NANIGNORE = ElementId("nanignore",272);
/// If the parameter is "on" return \b true, if "off" return \b false.
/// Any other value causes an exception.
@@ -128,6 +129,7 @@ OptionDatabase::OptionDatabase(Architecture *g)
registerOption(new OptionMaxInstruction());
registerOption(new OptionNamespaceStrategy());
registerOption(new OptionSplitDatatypes());
+ registerOption(new OptionNanIgnore());
}
OptionDatabase::~OptionDatabase(void)
@@ -985,4 +987,37 @@ string OptionSplitDatatypes::apply(Architecture *glb,const string &p1,const stri
return "Split data-type configuration set";
}
+string OptionNanIgnore::apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const
+
+{
+ bool oldIgnoreAll = glb->nan_ignore_all;
+ bool oldIgnoreCompare = glb->nan_ignore_compare;
+
+ if (p1 == "none") { // Don't ignore any NaN operation
+ glb->nan_ignore_all = false;
+ glb->nan_ignore_compare = false;
+ }
+ else if (p1 == "compare") { // Ignore only NaN operations protecting floating-point comparisons
+ glb->nan_ignore_all = false;
+ glb->nan_ignore_compare = true;
+ }
+ else if (p1 == "all") { // Ignore all NaN operations
+ glb->nan_ignore_all = true;
+ glb->nan_ignore_compare = true;
+ }
+ else {
+ throw LowlevelError("Unknown nanignore option: "+p1);
+ }
+ Action *root = glb->allacts.getCurrent();
+ if (!glb->nan_ignore_all && !glb->nan_ignore_compare) {
+ root->disableRule("ignorenan");
+ }
+ else {
+ root->enableRule("ignorenan");
+ }
+ if (oldIgnoreAll == glb->nan_ignore_all && oldIgnoreCompare == glb->nan_ignore_compare)
+ return "NaN ignore configuration unchanged";
+ return "Nan ignore configuration set to: " + p1;
+}
+
} // End namespace ghidra
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/options.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/options.hh
index 9c08babf21..018220a4e8 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/options.hh
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/options.hh
@@ -65,6 +65,7 @@ extern ElementId ELEM_STRUCTALIGN; ///< Marshaling element \
extern ElementId ELEM_TOGGLERULE; ///< Marshaling element \
extern ElementId ELEM_WARNING; ///< Marshaling element \
extern ElementId ELEM_JUMPTABLEMAX; ///< Marshaling element \
+extern ElementId ELEM_NANIGNORE; ///< Marshaling element \
/// \brief Base class for options classes that affect the configuration of the Architecture object
///
@@ -343,5 +344,11 @@ public:
virtual string apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const;
};
+class OptionNanIgnore : public ArchOption {
+public:
+ OptionNanIgnore(void) { name = "nanignore"; } ///< Constructor
+ virtual string apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const;
+};
+
} // End namespace ghidra
#endif
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc
index ae2e502c19..aee58417cd 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc
@@ -9437,26 +9437,151 @@ int4 RuleFloatCast::applyOp(PcodeOp *op,Funcdata &data)
}
/// \class RuleIgnoreNan
-/// \brief Treat FLOAT_NAN as always evaluating to false
+/// \brief Remove certain NaN operations by assuming their result is always \b false
///
-/// This makes the assumption that all floating-point calculations
-/// give valid results (not NaN).
+/// This rule can be configured to remove either all FLOAT_NAN operations or only those that
+/// protect floating-point comparisons.
void RuleIgnoreNan::getOpList(vector &oplist) const
{
oplist.push_back(CPUI_FLOAT_NAN);
}
+/// \brief Check if a boolean Varnode incorporates a floating-point comparison with the given value
+///
+/// The Varnode can either be the direct output of a comparison, or it can be a BOOL_OR or BOOL_AND,
+/// combining output from the comparison.
+/// \param floatVar is the given value the comparison must take as input
+/// \param root is the boolean Varnode
+/// \return \b true if the boolean Varnode incorporates the comparison
+bool RuleIgnoreNan::checkBackForCompare(Varnode *floatVar,Varnode *root)
+
+{
+ if (!root->isWritten()) return false;
+ PcodeOp *def1 = root->getDef();
+ if (!def1->isBoolOutput()) return false;
+ if (def1->getOpcode()->isFloatingPointOp()) {
+ if (def1->numInput() != 2) return false;
+ if (functionalEquality(floatVar, def1->getIn(0)))
+ return true;
+ if (functionalEquality(floatVar, def1->getIn(1)))
+ return true;
+ return false;
+ }
+ OpCode opc = def1->code();
+ if (opc != CPUI_BOOL_AND || opc != CPUI_BOOL_OR)
+ return false;
+ for(int4 i=0;i<2;++i) {
+ Varnode *vn = def1->getIn(i);
+ if (!vn->isWritten()) continue;
+ PcodeOp *def2 = vn->getDef();
+ if (!def2->isBoolOutput()) continue;
+ if (!def2->getOpcode()->isFloatingPointOp()) continue;
+ if (def2->numInput() != 2) continue;
+ if (functionalEquality(floatVar, def2->getIn(0)))
+ return true;
+ if (functionalEquality(floatVar, def2->getIn(1)))
+ return true;
+ }
+ return false;
+}
+
+/// \brief Test if a boolean expression incorporates a floating-point comparison, and remove the NaN data-flow if it does
+///
+/// The given PcodeOp takes input from a NaN operation through a specific slot. We look for a floating-point comparison
+/// PcodeOp (FLOAT_LESS, FLOAT_LESSEQUAL, FLOAT_EQUAL, or FLOAT_NOTEQUAL) that is combined with the given PcodeOp and
+/// has the same input Varnode as the NaN. The data-flow must be combined either through a BOOL_OR or BOOL_AND
+/// operation, or the given PcodeOp must be a CBRANCH that protects immediate control-flow to another CBRANCH
+/// taking the result of the comparison as input. If a matching comparison is found, the NaN input to the given
+/// PcodeOp is removed, assuming the output of the NaN operation is always \b false.
+/// Input from an unmodified NaN result must be combined through a BOOL_OR, but a NaN result that has been negated
+/// must combine through a BOOL_AND.
+/// \param floatVar is the input Varnode to NaN operation
+/// \param op is the given PcodeOp to test
+/// \param slot is the input index of the NaN operation
+/// \param matchCode is BOOL_AND if the NaN result has been negated, BOOL_OR if not
+/// \param count is incremented if a comparison is found and the NaN input is removed
+/// \param data is the function
+/// \return the output of the given PcodeOp if it has an opcode matching \b matchCode
+Varnode *RuleIgnoreNan::testForComparison(Varnode *floatVar,PcodeOp *op,int4 slot,OpCode matchCode,int4 &count,Funcdata &data)
+
+{
+ if (op->code() == matchCode) {
+ Varnode *vn = op->getIn(1-slot);
+ if (checkBackForCompare(floatVar,vn)) {
+ data.opSetOpcode(op, CPUI_COPY);
+ data.opRemoveInput(op, 1);
+ data.opSetInput(op, vn, 0);
+ count += 1;
+ }
+ return op->getOut();
+ }
+ if (op->code() != CPUI_CBRANCH)
+ return (Varnode *)0;
+ BlockBasic *parent = op->getParent();
+ bool flowToFromCompare = false;
+ PcodeOp *lastOp;
+ int4 outDir = (matchCode == CPUI_BOOL_OR) ? 0 : 1;
+ if (op->isBooleanFlip())
+ outDir = 1 - outDir;
+ FlowBlock *outBranch = parent->getOut(outDir);
+ lastOp = outBranch->lastOp();
+ if (lastOp != (PcodeOp *)0 && lastOp->code() == CPUI_CBRANCH) {
+ FlowBlock *otherBranch = parent->getOut(1-outDir);
+ if (outBranch->getOut(0) == otherBranch || outBranch->getOut(1) == otherBranch) {
+ if (checkBackForCompare(floatVar, lastOp->getIn(1)))
+ flowToFromCompare = true;
+ }
+ }
+ if (flowToFromCompare) {
+ data.opSetInput(op,data.newConstant(1, 0),1); // Treat result of NaN as false
+ count += 1;
+ }
+ return (Varnode *)0;
+}
+
int4 RuleIgnoreNan::applyOp(PcodeOp *op,Funcdata &data)
{
- if (op->numInput()==2)
- data.opRemoveInput(op,1);
-
- // Treat these operations as always returning false (0)
- data.opSetOpcode(op,CPUI_COPY);
- data.opSetInput(op,data.newConstant(1,0),0);
- return 1;
+ if (data.getArch()->nan_ignore_all) {
+ // Treat these NaN operation as always returning false (0)
+ data.opSetOpcode(op,CPUI_COPY);
+ data.opSetInput(op,data.newConstant(1,0),0);
+ return 1;
+ }
+ Varnode *floatVar = op->getIn(0);
+ if (floatVar->isFree()) return 0;
+ Varnode *out1 = op->getOut();
+ int4 count = 0;
+ list::const_iterator iter1 = out1->beginDescend();
+ while(iter1 != out1->endDescend()) {
+ PcodeOp *boolRead1 = *iter1;
+ ++iter1; // out1 may be truncated from boolRead1 below, advance iterator now
+ Varnode *out2;
+ OpCode matchCode = CPUI_BOOL_OR;
+ if (boolRead1->code() == CPUI_BOOL_NEGATE) {
+ matchCode = CPUI_BOOL_AND;
+ out2 = boolRead1->getOut();
+ }
+ else {
+ out2 = testForComparison(floatVar, boolRead1, boolRead1->getSlot(out1), matchCode, count, data);
+ }
+ if (out2 == (Varnode *)0) continue;
+ list::const_iterator iter2 = out2->beginDescend();
+ while(iter2 != out2->endDescend()) {
+ PcodeOp *boolRead2 = *iter2;
+ ++iter2;
+ Varnode *out3 = testForComparison(floatVar,boolRead2, boolRead2->getSlot(out2), matchCode, count, data);
+ if (out3 == (Varnode *)0) continue;
+ list::const_iterator iter3 = out3->beginDescend();
+ while(iter3 != out3->endDescend()) {
+ PcodeOp *boolRead3 = *iter3;
+ ++iter3;
+ testForComparison(floatVar, boolRead3, boolRead3->getSlot(out3), matchCode, count, data);
+ }
+ }
+ }
+ return (count > 0) ? 1 : 0;
}
/// \class RuleFuncPtrEncoding
diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh
index 1988db3fb0..2d19dc5feb 100644
--- a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh
+++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh
@@ -1549,6 +1549,8 @@ public:
};
class RuleIgnoreNan : public Rule {
+ static bool checkBackForCompare(Varnode *floatVar,Varnode *root);
+ static Varnode *testForComparison(Varnode *floatVar,PcodeOp *op,int4 slot,OpCode matchCode,int4 &count,Funcdata &data);
public:
RuleIgnoreNan(const string &g) : Rule( g, 0, "ignorenan") {} ///< Constructor
virtual Rule *clone(const ActionGroupList &grouplist) const {
diff --git a/Ghidra/Features/Decompiler/src/decompile/datatests/nan.xml b/Ghidra/Features/Decompiler/src/decompile/datatests/nan.xml
new file mode 100644
index 0000000000..07eb971eaa
--- /dev/null
+++ b/Ghidra/Features/Decompiler/src/decompile/datatests/nan.xml
@@ -0,0 +1,30 @@
+
+
+
+
+ f30f1efa4883ec18f2
+0f114c2408bf01000000660f2ec07a08
+b8000000000f44f8e88cfffffff20f10
+05230e0000660f2f442408400f97c740
+0fb6ffe898ffffff4883c418c3
+
+
+ 000000000000e83f
+
+
+
+
+
+
+read_nan\(NAN\(param_1\)\);
+read_compare\(param_2 < 0\.75\);
+NAN\(param_2\)
+
diff --git a/Ghidra/Features/Decompiler/src/main/doc/decompileplugin.xml b/Ghidra/Features/Decompiler/src/main/doc/decompileplugin.xml
index 816c0d771b..11c1c94a03 100644
--- a/Ghidra/Features/Decompiler/src/main/doc/decompileplugin.xml
+++ b/Ghidra/Features/Decompiler/src/main/doc/decompileplugin.xml
@@ -3088,6 +3088,35 @@
+
+ NaN operations
+
+
+ This option determines how the Decompiler treats floating-point NaN
+ (Not a Number) operations. Many processors automatically perform NaN checks on the operands of
+ floating-point instructions, and unless specifically configured, these show up in Decompiler output
+ as NAN() functional tokens. Common floating-point source code operations, like
+ <, >, and == can generate NAN tokens as a
+ side effect, even if the original source code was not designed to handle NaN values, and the
+ tokens can clutter the output.
+
+
+ The user can optionally configure some or all of the NaN operations to be ignored, meaning that
+ inputs to the NaN operation are assumed to be valid floating-point values and the
+ NAN function is removed, replacing it with the value: false.
+ The possible settings are:
+
+
+ Ignore none - No NaN operations are removed
+ Ignore with comparisons - NaN operations associated with comparisons are removed
+ Ignore all - All NaN operations are removed
+
+
+ The Decompiler considers a NaN operation to be associated with a floating-point comparison if they both
+ can be considered boolean clauses of the same if condition.
+
+
+ Recover -for- loops
diff --git a/Ghidra/Features/Decompiler/src/main/help/help/topics/DecompilePlugin/DecompilerOptions.html b/Ghidra/Features/Decompiler/src/main/help/help/topics/DecompilePlugin/DecompilerOptions.html
index 2742292129..a2996b1cd7 100644
--- a/Ghidra/Features/Decompiler/src/main/help/help/topics/DecompilePlugin/DecompilerOptions.html
+++ b/Ghidra/Features/Decompiler/src/main/help/help/topics/DecompilePlugin/DecompilerOptions.html
@@ -4,7 +4,7 @@
Decompiler Options
-
+
@@ -241,6 +241,40 @@
+NaN operations
+
+
+
+ This option determines how the Decompiler treats floating-point NaN
+ (Not a Number) operations. Many processors automatically perform NaN checks on the operands of
+ floating-point instructions, and unless specifically configured, these show up in Decompiler output
+ as NAN() functional tokens. Common floating-point source code operations, like
+ <, >, and == can generate NAN tokens as a
+ side effect, even if the original source code was not designed to handle NaN values, and the
+ tokens can clutter the output.
+
+
+ The user can optionally configure some or all of the NaN operations to be ignored, meaning that
+ inputs to the NaN operation are assumed to be valid floating-point values and the
+ NAN function is removed, replacing it with the value: false.
+ The possible settings are:
+
+
+
+
+Ignore none - No NaN operations are removed
+
+Ignore with comparisons - NaN operations associated with comparisons are removed
+
+Ignore all - All NaN operations are removed
+
+
+
+ The Decompiler considers a NaN operation to be associated with a floating-point comparison if they both
+ can be considered boolean clauses of the same if condition.
+
+
+
Recover -for- loops
diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileOptions.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileOptions.java
index d7e7ff9120..0800d12c9a 100644
--- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileOptions.java
+++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileOptions.java
@@ -124,6 +124,37 @@ public class DecompileOptions {
private final static boolean SPLITPOINTERS_OPTIONDEFAULT = true; // Must match Architecture::resetDefaultsInternal
private boolean splitPointers;
+ private final static String NANIGNORE_OPTIONSTRING = "Analysis.NaN operations";
+ private final static String NANIGNORE_OPTIONDESCRIPTION =
+ "Specify how much to ignore floating-point NaN operations in decompiler output";
+
+ public enum NanIgnoreEnum {
+
+ None("none", "Ignore none"),
+ Compare("compare", "Ignore with comparisons"),
+ All("all", "Ignore all");
+
+ private String label;
+ private String optionString;
+
+ private NanIgnoreEnum(String optString, String label) {
+ this.label = label;
+ this.optionString = optString;
+ }
+
+ public String getOptionString() {
+ return optionString;
+ }
+
+ @Override
+ public String toString() {
+ return label;
+ }
+ }
+
+ private final static NanIgnoreEnum NANIGNORE_OPTIONDEFAULT = NanIgnoreEnum.Compare; // Must match Architecture::resetDefaultsInternal
+ private NanIgnoreEnum nanIgnore;
+
private final static String NULLTOKEN_OPTIONSTRING = "Display.Print 'NULL' for null pointers";
private final static String NULLTOKEN_OPTIONDESCRIPTION =
"If set, any zero valued pointer (null pointer) will " +
@@ -412,6 +443,7 @@ public class DecompileOptions {
splitStructures = SPLITSTRUCTURES_OPTIONDEFAULT;
splitArrays = SPLITARRAYS_OPTIONDEFAULT;
splitPointers = SPLITPOINTERS_OPTIONDEFAULT;
+ nanIgnore = NANIGNORE_OPTIONDEFAULT;
ignoreunimpl = IGNOREUNIMPL_OPTIONDEFAULT;
inferconstptr = INFERCONSTPTR_OPTIONDEFAULT;
analyzeForLoops = ANALYZEFORLOOPS_OPTIONDEFAULT;
@@ -473,6 +505,7 @@ public class DecompileOptions {
opt.getBoolean(SPLITSTRUCTURES_OPTIONSTRING, SPLITSTRUCTURES_OPTIONDEFAULT);
splitArrays = opt.getBoolean(SPLITARRAYS_OPTIONSTRING, SPLITARRAYS_OPTIONDEFAULT);
splitPointers = opt.getBoolean(SPLITPOINTERS_OPTIONSTRING, SPLITPOINTERS_OPTIONDEFAULT);
+ nanIgnore = opt.getEnum(NANIGNORE_OPTIONSTRING, NANIGNORE_OPTIONDEFAULT);
nullToken = opt.getBoolean(NULLTOKEN_OPTIONSTRING, NULLTOKEN_OPTIONDEFAULT);
inplaceTokens = opt.getBoolean(INPLACEOP_OPTIONSTRING, INPLACEOP_OPTIONDEFAULT);
@@ -592,6 +625,9 @@ public class DecompileOptions {
opt.registerOption(SPLITPOINTERS_OPTIONSTRING, SPLITPOINTERS_OPTIONDEFAULT,
new HelpLocation(HelpTopics.DECOMPILER, "AnalysisSplitPointers"),
SPLITPOINTERS_OPTIONDESCRIPTION);
+ opt.registerOption(NANIGNORE_OPTIONSTRING, NANIGNORE_OPTIONDEFAULT,
+ new HelpLocation(HelpTopics.DECOMPILER, "AnalysisNanIgnore"),
+ NANIGNORE_OPTIONDESCRIPTION);
opt.registerOption(NULLTOKEN_OPTIONSTRING, NULLTOKEN_OPTIONDEFAULT,
new HelpLocation(HelpTopics.DECOMPILER, "DisplayNull"), NULLTOKEN_OPTIONDESCRIPTION);
opt.registerOption(INPLACEOP_OPTIONSTRING, INPLACEOP_OPTIONDEFAULT,
@@ -758,6 +794,9 @@ public class DecompileOptions {
String p3 = splitPointers ? "pointer" : "";
appendOption(encoder, ELEM_SPLITDATATYPE, p1, p2, p3);
}
+ if (nanIgnore != NANIGNORE_OPTIONDEFAULT) {
+ appendOption(encoder, ELEM_NANIGNORE, nanIgnore.getOptionString(), "", "");
+ }
appendOption(encoder, ELEM_READONLY, readOnly ? "on" : "off", "", "");
// Must set language early so that the object is in place before other option changes
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/ElementId.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/ElementId.java
index d7a813c678..15c0968f24 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/ElementId.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/ElementId.java
@@ -423,5 +423,6 @@ public record ElementId(String name, int id) {
public static final ElementId ELEM_SPLITDATATYPE = new ElementId("splitdatatype", 270);
public static final ElementId ELEM_JUMPTABLEMAX = new ElementId("jumptablemax", 271);
- public static final ElementId ELEM_UNKNOWN = new ElementId("XMLunknown", 272);
+ public static final ElementId ELEM_NANIGNORE = new ElementId("nanignore", 272);
+ public static final ElementId ELEM_UNKNOWN = new ElementId("XMLunknown", 273);
}