Both HIR and LIR blocks are composed of `GenTree` nodes that define the operations performed by the block. A `GenTree` node may consume some number of operands and may produce a singly-defined, at-most-singly-used value as a result. These values are referred to interchangably as *SDSU* temps or *tree* temps. Defs of SDSU temps are represented by `GenTree` nodes themselves, and uses are represented by edges from the using node to the defining node. Furthermore, SDSU temps defined in one block may not be used in a different block. In cases where a value must be multiply-defined, multiply-used, or defined in one block and used in another, the IR provides another class of temporary: the local var. Local vars are defined by assignment nodes in HIR or store nodes in LIR, and are used by local var nodes in both forms.
-An HIR block is composed of a doubly-linked list of statement nodes (`GenTreeStmt`), each of which references a single expression tree (`gtStmtExpr`). The `GenTree` nodes in this tree execute in "tree order", which is defined as the order produced by a depth-first, left-to-right traversal of the tree, with two notable exceptions:
+An HIR block is composed of a doubly-linked list of statement nodes (`Statement`), each of which references a single expression tree (`m_rootNode`). The `GenTree` nodes in this tree execute in "tree order", which is defined as the order produced by a depth-first, left-to-right traversal of the tree, with two notable exceptions:
- Binary nodes marked with the `GTF_REVERSE_OPS` flag execute their right operand tree (`gtOp2`) before their left operand tree (`gtOp1`)
- Dynamically-sized block copy nodes where `gtEvalSizeFirst` is `true` execute the `gtDynamicSize` tree before executing their other operand trees.
`GenTree` nodes are doubly-linked in execution order, but the links are not necessarily valid during all phases of the JIT. In HIR these links are primarily a convenience, as the order produced by a traversal of the links must match the order produced by a "tree order" traversal (see above for details). In LIR these links define the execution order of the nodes.
HIR statement nodes utilize the same `GenTree` base type as the operation nodes, though they are not truly related.
-* The statement nodes are doubly-linked. The first statement node in a block points to the last node in the block via its `gtPrev` link. Note that the last statement node does *not* point to the first; that is, the list is not fully circular.
-* Each statement node contains two `GenTree` links – `gtStmtExpr` points to the top-level node in the statement (i.e. the root of the tree that represents the statement), while `gtStmtList` points to the first node in execution order (again, this link is not always valid).
+* The statement nodes are doubly-linked. The first statement node in a block points to the last node in the block via its `m_prev` link. Note that the last statement node does *not* point to the first; that is, the list is not fully circular.
+* Each statement node contains two `GenTree` links – `m_rootNode` points to the top-level node in the statement (i.e. the root of the tree that represents the statement), while `m_treeList` points to the first node in execution order (again, this link is not always valid).
## Local var descriptors
A stripped-down dump of the `GenTree` nodes just after they are imported looks like this:
```
-▌ STMT void (IL 0x000...0x026)
-└──▌ ASG double
+STMT (IL 0x000...0x026)
+ ▌ ASG double
├──▌ IND double
│ └──▌ LCL_VAR byref V03 arg3
└──▌ DIV double
| **Phase** | **IR Transformations** |
| --- | --- |
|[Pre-import](#pre-import)|`Compiler->lvaTable` created and filled in for each user argument and variable. BasicBlock list initialized.|
-|[Importation](#importation)|`GenTree` nodes created and linked in to Statements, and Statements into BasicBlocks. Inlining candidates identified.|
+|[Importation](#importation)|`GenTree` nodes created and linked in to `Statement` nodes, and Statements into BasicBlocks. Inlining candidates identified.|
|[Inlining](#inlining)|The IR for inlined methods is incorporated into the flowgraph.|
|[Struct Promotion](#struct-promotion)|New lclVars are created for each field of a promoted struct.|
|[Mark Address-Exposed Locals](#mark-addr-exposed)|lclVars with references occurring in an address-taken context are marked. This must be kept up-to-date.|
For our earlier example (Example of Post-Import IR), here is what the simplified dump looks like just prior to Rationalization (the $ annotations are value numbers). Note that some common subexpressions have been computed into new temporary lclVars, and that computation has been inserted as a `GT_COMMA` (comma) node in the IR:
```
-▌ STMT void (IL 0x000...0x026)
-└──▌ ASG double $VN.Void
+STMT (IL 0x000...0x026)
+ ▌ ASG double $VN.Void
├──▌ IND double $146
│ └──▌ LCL_VAR byref V03 arg3 u:1 (last use) $c0
└──▌ DIV double $146
└──▌ LCL_VAR double V07 cse1 $145
```
-After rationalization, the nodes are presented in execution order, and the `GT_COMMA` (comma), `GT_ASG` (=), and `GT_STMT` nodes have been eliminated:
+After rationalization, the nodes are presented in execution order, and the `GT_COMMA` (comma), `GT_ASG` (=), and `Statement` nodes have been eliminated:
```
IL_OFFSET void IL offset: 0x0
* Rationalization
* All `GT_ASG` trees are transformed into `GT_STORE` variants (e.g. `GT_STORE_LCL_VAR`).
* All `GT_ADDR` nodes are eliminated (e.g. with `GT_LCL_VAR_ADDR`).
- * All `GT_COMMA` and `GT_STMT` nodes are removed and their constituent nodes linked into execution order.
+ * All `GT_COMMA` and `Statement` nodes are removed and their constituent nodes linked into execution order.
* Lowering
* `GenTree` nodes are split or transformed as needed to expose all of their register requirements and any necessary `flowgraph` changes (e.g., for switch statements).
Ordering:
-* For `GenTreeStmt` nodes, the `gtNext` and `gtPrev` fields must always be consistent. The last statement in the `BasicBlock` must have `gtNext` equal to null. By convention, the `gtPrev` of the first statement in the `BasicBlock` must be the last statement of the `BasicBlock`.
- * In all phases, `gtStmtExpr` points to the top-level node of the expression.
-* For non-statement nodes, the `gtNext` and `gtPrev` fields are either null, prior to ordering, or they are consistent (i.e. `A->gtPrev->gtNext = A`, and `A->gtNext->gtPrev == A`, if they are non-null).
-* After normalization the `gtStmtList` of the containing statement points to the first node to be executed.
-* Prior to normalization, the `gtNext` and `gtPrev` pointers on the expression (non-statement) `GenTree` nodes are invalid. The expression nodes are only traversed via the links from parent to child (e.g. `node->gtGetOp1()`, or `node->gtOp.gtOp1`). The `gtNext/gtPrev` links are set by `fgSetBlockOrder()`.
+* For `Statement` nodes, the `m_next` and `m_prev` fields must always be consistent. The last statement in the `BasicBlock` must have `m_next` equal to null. By convention, the `m_prev` of the first statement in the `BasicBlock` must be the last statement of the `BasicBlock`.
+ * In all phases, `m_rootNode` points to the top-level node of the expression.
+* For 'GenTree' nodes, the `gtNext` and `gtPrev` fields are either null, prior to ordering, or they are consistent (i.e. `A->gtPrev->gtNext = A`, and `A->gtNext->gtPrev == A`, if they are non-null).
+* After normalization the `m_treeList` of the containing statement points to the first node to be executed.
+* Prior to normalization, the `gtNext` and `gtPrev` pointers on the expression `GenTree` nodes are invalid. The expression nodes are only traversed via the links from parent to child (e.g. `node->gtGetOp1()`, or `node->gtOp.gtOp1`). The `gtNext/gtPrev` links are set by `fgSetBlockOrder()`.
* After normalization, and prior to rationalization, the parent/child links remain the primary traversal mechanism. The evaluation order of any nested expression-statements (usually assignments) is enforced by the `GT_COMMA` in which they are contained.
* After rationalization, all `GT_COMMA` nodes are eliminated, statements are flattened, and the primary traversal mechanism becomes the `gtNext/gtPrev` links which define the execution order.
* In tree ordering:
- * The `gtPrev` of the first node (`gtStmtList`) is always null.
- * The `gtNext` of the last node (`gtStmtExpr`) is always null.
+ * The `gtPrev` of the first node (`m_treeList`) is always null.
+ * The `gtNext` of the last node (`m_rootNode`) is always null.
## LclVar phase-dependent properties
Debug info consists primarily of two types of information in the JIT:
* Mapping of IL offsets to native code offsets. This is accomplished via:
- * the `gtStmtILoffsx` on the statement nodes (`GenTreeStmt`)
+ * the `m_ILOffsetX` on the statement nodes (`Statement`)
* the `gtLclILoffs` on lclVar references (`GenTreeLclVar`)
* The IL offsets are captured during CodeGen by calling `CodeGen::genIPmappingAdd()`, and then written to debug tables by `CodeGen::genIPmappingGen()`.
* Mapping of user locals to location (register or stack). This is accomplished via:
Here is a full dump of an entire statement:
```
-[000026] ------------ ▌ STMT void (IL 0x010... ???)
+STMT00000 (IL 0x010... ???)
[000025] --C-G------- └──▌ RETURN double
[000023] --C-G------- └──▌ CALL double C.DblSqrt
[000022] ------------ arg0 └──▌ MUL double
- `LclVarDsc` represents a local variable, argument or JIT-created temp. It has a `gtLclNum` which is the identifier usually associated with the variable in the JIT and its dumps. The `LclVarDsc` contains the type, use count, weighted use count, frame or register assignment etc. These are often referred to simply as “lclVars”. They can be tracked (`lvTracked`), in which case they participate in dataflow analysis, and have a different index (`lvVarIndex`) to allow for the use of dense bit vectors. Only non-address-taken lclVars participate in liveness analysis, though aliased variables can participate in value numbering.
### GenTrees
-- A `BasicBlock` is a list of statements (GenTreeStmt nodes)
- - It has a pointer to the expression for the statement
+- A `BasicBlock` is a list of statements (`Statement`)
+ - It has a pointer to the root expression for the statement
- Statement nodes share the same base type as expression nodes, though they are really distinct IR objects
-- Each `GenTreeStmt` node points to its expression tree
+- Each `Statement` node points to its root expression tree
- Each node points to its operands (children), and contains:
- Oper (the operator for the expression, e.g. GT_ASG, GT_ADD, …)
- Type (the evaluation type, e.g. GT_INT, GT_REF, GT_STRUCT)
- Comma nodes are inserted to allow creation of (multi-use) temps while preserving ordering constraints
#### Notes
-The GenTree is the primary data structure of the JIT. It is used to represent both the statements within a block, as well as the expressions for each statement.
+The GenTree is the primary data structure of the JIT. It is used to represent the expressions for each statement.
Some distinguishing features of this IR are that, while an operation has links to its operands, they do not have a link to their parent expression.
Furthermore, during the initial phases of the JIT, the nodes are only ordered implicitly by the canonical traversal order of trees.
The initial construction of the IR ensures that any ordering dependencies are obeyed by the canonical traversal order, and any subsequent optimizations must ensure that any visible ordering constraints are obeyed.
### GenTrees Sample
```
-▌ stmtExpr void (top level) (IL 0x01D
+▌ Statement (top level) (IL 0x01D
│ ┌──▌ const int 1
│ ┌──▌ & int
│ │ └──▌ lclVar int V08
- Execution order links (`gtPrev` and `gtNext`) are the definitive specification
of execution order (no longer derivable from a tree walk)
- Each `BasicBlock` contains a single linked list of nodes
- - `GT_STMT` nodes are eliminated
+ - `Statement` nodes are eliminated
- `GT_IL_OFFSET`nodes convey source (IL) mapping info
#### Notes
### IR Rationalization: Commas
#### Front-end IR
```
-▌ stmtExpr (IL 0x093...0x09D)
+▌ Statement (IL 0x093...0x09D)
│ ┌──▌ lclVar long V09
│ ┌──▌ indir long
│ │ ┌──▌ lclFld float V10 [+0]
```
##### Rationalized IR
```
-▌ stmtExpr (IL 0x093...0x09D)
+▌ Statement (IL 0x093...0x09D)
│ ┌──▌ lclVar long V09
│ │ { ▌ stmtExpr (embedded)
│ │ { │ ┌──▌ &lclFld V10 [+0]
### IR Dump: Front-end
Here is an example dump in tree order (shown with COMPlus_JitDumpAscii=0)
```
-[000068] ------------ ▌ stmtExpr void (top level) (IL ???... ???)
-[000067] -AC-G------- â\94\94â\94\80â\94\80â\96\8c call help void HELPER.CORINFO_HELP_ARRADDR_ST
-[000047] ------------ arg0 ├──▌ lclVar ref V03 loc2
-[000048] ------------ arg1 ├──▌ const int 0
-[000063] -A---------- arg2 └──▌ box ref
-[000061] ------------ │ ┌──▌ lclVar ref V04 tmp0
-[000062] -A---------- └──▌ comma ref
-[000049] ------------ │ ┌──▌ lclVar long V01 loc0
-[000060] -A---------- └──▌ = long
-[000059] -------N---- └──▌ indir long
-[000057] ------------ │ ┌──▌ const long 8
-[000058] ------------ └──▌ + byref
-[000056] ------------ └──▌ lclVar ref V04 tmp0
+STMT00000 (IL ???... ???)
+[000067] -AC-G------- ▌ call help void HELPER.CORINFO_HELP_ARRADDR_ST
+[000047] ------------ arg0 ├──▌ lclVar ref V03 loc2
+[000048] ------------ arg1 ├──▌ const int 0
+[000063] -A---------- arg2 └──▌ box ref
+[000061] ------------ │ ┌──▌ lclVar ref V04 tmp0
+[000062] -A---------- └──▌ comma ref
+[000049] ------------ │ ┌──▌ lclVar long V01 loc0
+[000060] -A---------- └──▌ = long
+[000059] -------N---- └──▌ indir long
+[000057] ------------ │ ┌──▌ const long 8
+[000058] ------------ └──▌ + byref
+[000056] ------------ └──▌ lclVar ref V04 tmp0
```
### IR Dump: Back-end
N233 ( 1, 1) [000420] ------------ arg1 in rdx │ ├──▌ putarg_reg int REG rdx
N241 ( 49, 31) [000067] -ACXGO------ └──▌ call help void HELPER.CORINFO_HELP_ARRADDR_ST $1d1
```
+#### Notes
+This needs to be updated, as we no longer have statements in back-end.
### Phase Transitions
- Flowgraph analysis
optAddCopyLclNum = lclNum; // in
optAddCopyAsgnNode = nullptr; // out
- fgWalkTreePre(&stmt->gtStmtExpr, Compiler::optAddCopiesCallback, (void*)this, false);
+ fgWalkTreePre(stmt->GetRootNodePointer(), Compiler::optAddCopiesCallback, (void*)this, false);
noway_assert(optAddCopyAsgnNode);
if (verbose)
{
printf("\nIntroducing a new copy for V%02u\n", lclNum);
- gtDispTree(stmt->gtStmtExpr);
+ gtDispTree(stmt->GetRootNode());
printf("\n");
}
#endif
{
// If there's no parent, the tree being replaced is the root of the
// statement.
- assert((stmt->gtStmtExpr == tree) && (&stmt->gtStmtExpr == useEdge));
- stmt->gtStmtExpr = newTree;
+ assert((stmt->GetRootNode() == tree) && (stmt->GetRootNodePointer() == useEdge));
+ stmt->SetRootNode(newTree);
}
// We only need to ensure that the gtNext field is set as it is used to traverse
// Walk the statement trees in this basic block.
for (Statement* stmt : block->Statements())
{
- for (GenTree* tree = stmt->gtStmtList; tree != nullptr; tree = tree->gtNext)
+ for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext)
{
if (tree->gtOper == GT_JTRUE)
{
// A GT_TRUE is always the last node in a tree, so we can break here
- assert((tree->gtNext == nullptr) && (stmt->gtNext == nullptr));
+ assert((tree->gtNext == nullptr) && (stmt->GetNextStmt() == nullptr));
jtrue = tree;
break;
}
}
// Preserve the prev link before the propagation and morph.
- Statement* prev = (stmt == block->firstStmt()) ? nullptr : stmt->getPrevStmt();
+ Statement* prev = (stmt == block->firstStmt()) ? nullptr : stmt->GetPrevStmt();
// Perform VN based assertion prop first, in case we don't find
// anything in assertion gen.
optAssertionPropagatedCurrentStmt = false;
VNAssertionPropVisitorInfo data(this, block, stmt);
- fgWalkTreePre(&stmt->gtStmtExpr, Compiler::optVNAssertionPropCurStmtVisitor, &data);
+ fgWalkTreePre(stmt->GetRootNodePointer(), Compiler::optVNAssertionPropCurStmtVisitor, &data);
if (optAssertionPropagatedCurrentStmt)
{
}
// Perform assertion gen for control flow based assertions.
- for (GenTree* tree = stmt->gtStmtList; tree != nullptr; tree = tree->gtNext)
+ for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext)
{
optAssertionGen(tree);
}
// Preserve the prev link before the propagation and morph, to check if propagation
// removes the current stmt.
- Statement* prevStmt = (stmt == block->firstStmt()) ? nullptr : stmt->getPrevStmt();
+ Statement* prevStmt = (stmt == block->firstStmt()) ? nullptr : stmt->GetPrevStmt();
optAssertionPropagatedCurrentStmt = false; // set to true if a assertion propagation took place
// and thus we must morph, set order, re-link
- for (GenTree* tree = stmt->gtStmtList; tree != nullptr; tree = tree->gtNext)
+ for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext)
{
if (tree->OperIs(GT_JTRUE))
{
// A GT_TRUE is always the last node in a tree, so we can break here
- assert((tree->gtNext == nullptr) && (stmt->gtNext == nullptr));
+ assert((tree->gtNext == nullptr) && (stmt->GetNextStmt() == nullptr));
break;
}
for (Statement* fromStmt : from->Statements())
{
- auto newExpr = compiler->gtCloneExpr(fromStmt->gtStmtExpr, 0, varNum, varVal);
+ auto newExpr = compiler->gtCloneExpr(fromStmt->GetRootNode(), 0, varNum, varVal);
if (!newExpr)
{
// gtCloneExpr doesn't handle all opcodes, so may fail to clone a statement.
return nullptr;
}
- Statement* result = bbStmtList->gtPrevStmt;
- assert(result != nullptr && result->gtNext == nullptr);
+ Statement* result = bbStmtList->GetPrevStmt();
+ assert(result != nullptr && result->GetNextStmt() == nullptr);
return result;
}
//
GenTree* BasicBlock::firstNode()
{
- return IsLIR() ? GetFirstLIRNode() : Compiler::fgGetFirstNode(firstStmt()->gtStmtExpr);
+ return IsLIR() ? GetFirstLIRNode() : Compiler::fgGetFirstNode(firstStmt()->GetRootNode());
}
//------------------------------------------------------------------------
//
GenTree* BasicBlock::lastNode()
{
- return IsLIR() ? m_lastNode : lastStmt()->gtStmtExpr;
+ return IsLIR() ? m_lastNode : lastStmt()->GetRootNode();
}
//------------------------------------------------------------------------
{
return nullptr;
}
- GenTree* tree = stmt->gtStmtExpr;
+ GenTree* tree = stmt->GetRootNode();
while ((tree->OperGet() == GT_ASG && tree->gtOp.gtOp2->OperGet() == GT_PHI) ||
(tree->OperGet() == GT_STORE_LCL_VAR && tree->gtOp.gtOp1->OperGet() == GT_PHI))
{
{
return nullptr;
}
- tree = stmt->gtStmtExpr;
+ tree = stmt->GetRootNode();
}
return stmt;
}
{
return nullptr;
}
- GenTree* tree = stmt->gtStmtExpr;
+ GenTree* tree = stmt->GetRootNode();
if ((tree->OperGet() == GT_ASG && tree->gtOp.gtOp2->OperGet() == GT_CATCH_ARG) ||
(tree->OperGet() == GT_STORE_LCL_VAR && tree->gtOp.gtOp1->OperGet() == GT_CATCH_ARG))
{
if ((block->bbRefs > 1) && (stmt != nullptr))
{
bool found = false;
- if (stmt->gtStmtILoffsx != BAD_IL_OFFSET)
+ if (stmt->GetILOffsetX() != BAD_IL_OFFSET)
{
- IL_OFFSET ilOffs = jitGetILoffs(stmt->gtStmtILoffsx);
+ IL_OFFSET ilOffs = jitGetILoffs(stmt->GetILOffsetX());
for (unsigned i = 0; i < eeBoundariesCount; ++i)
{
if (eeBoundaries[i].ilOffset == ilOffs)
{
for (Statement* stmt : block->Statements())
{
- for (GenTree* tree = stmt->gtStmtList; tree != nullptr; tree = tree->gtNext)
+ for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext)
{
tree->ClearVN();
tree->ClearAssertion();
{
for (Statement* stmt = block->FirstNonPhiDef(); stmt != nullptr; stmt = stmt->GetNextStmt())
{
- for (GenTree* tree = stmt->gtStmtList; tree != nullptr; tree = tree->gtNext)
+ for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext)
{
TestLabelAndNum tlAndN;
{
for (Statement* stmt : block->Statements())
{
- for (GenTree* call = stmt->gtStmtList; call != nullptr; call = call->gtNext)
+ for (GenTree* call = stmt->GetTreeList(); call != nullptr; call = call->gtNext)
{
if (call->gtOper != GT_CALL)
continue;
{
for (Statement* stmt : block->Statements())
{
- tree = dFindTree(stmt->gtStmtExpr, id);
+ tree = dFindTree(stmt->GetRootNode(), id);
if (tree != nullptr)
{
dbTreeBlock = block;
if (comp->compRationalIRForm)
{
- for (GenTree* tree = stmt->gtStmtList; tree != nullptr; tree = tree->gtNext)
+ for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext)
{
cNodeIR(comp, tree);
}
void cStmtIR(Compiler* comp, Statement* stmt)
{
- cTreeIR(comp, stmt->gtStmtExpr);
+ cTreeIR(comp, stmt->GetRootNode());
if (!comp->dumpIRNoStmts)
{
dTabStopIR(0, COLUMN_OPCODE);
Statement* gtCloneStmt(Statement* stmt)
{
- GenTree* exprClone = gtCloneExpr(stmt->gtStmtExpr);
- return gtNewStmt(exprClone, stmt->gtStmtILoffsx);
+ GenTree* exprClone = gtCloneExpr(stmt->GetRootNode());
+ return gtNewStmt(exprClone, stmt->GetILOffsetX());
}
// Internal helper for cloning a call
bool impNestedStackSpill;
// For displaying instrs with generated native code (-n:B)
- Statement* impLastILoffsStmt; // oldest stmt added for which we did not gtStmtLastILoffs
+ Statement* impLastILoffsStmt; // oldest stmt added for which we did not call SetLastILOffset().
void impNoteLastILoffs();
#endif
inline void Compiler::gtSetStmtInfo(Statement* stmt)
{
- GenTree* expr = stmt->gtStmtExpr;
+ GenTree* expr = stmt->GetRootNode();
/* Recursively process the expression */
assert(fgStmtListThreaded);
- for (GenTree* tree = stmt->gtStmtList; tree != nullptr; tree = tree->gtNext)
+ for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext)
{
if (tree->gtOper != GT_LCL_VAR)
{
{
for (Statement* stmt : block->Statements())
{
- for (GenTree* tree = stmt->gtStmtList; tree != nullptr; tree = tree->gtNext)
+ for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext)
{
if (!tree->IsLocal())
{
VarSetOps::ClearD(this, optCopyPropKillSet);
// Walk the tree to find if any local variable can be replaced with current live definitions.
- for (GenTree* tree = stmt->gtStmtList; tree != nullptr; tree = tree->gtNext)
+ for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext)
{
treeLifeUpdater.UpdateLife(tree);
}
// This logic must be in sync with SSA renaming process.
- for (GenTree* tree = stmt->gtStmtList; tree != nullptr; tree = tree->gtNext)
+ for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext)
{
if (!optIsSsaLocal(tree))
{
// Walk the stmt tree in linear order to rewrite any array length reference with a
// constant array length.
bool isRewritten = false;
- for (GenTree* tree = stmt->gtStmtList; tree != nullptr; tree = tree->gtNext)
+ for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext)
{
GenTree* rewrittenTree = optEarlyPropRewriteTree(tree);
if (rewrittenTree != nullptr)
// * stmtExpr void (top level)
// \--* indir int
// \--* lclVar ref V02 loc0
- if (compCurStmt->gtStmtExpr == tree)
+ if (compCurStmt->GetRootNode() == tree)
{
return nullptr;
}
// Then walk the statement list in reverse execution order
// until we get to the statement containing the null check.
// We only need to check the side effects at the root of each statement.
- Statement* curStmt = compCurStmt->getPrevStmt();
- currentTree = curStmt->gtStmtExpr;
+ Statement* curStmt = compCurStmt->GetPrevStmt();
+ currentTree = curStmt->GetRootNode();
while (canRemoveNullCheck && (currentTree != defParent))
{
if ((nodesWalked++ > maxNodesWalked) ||
}
else
{
- curStmt = curStmt->getPrevStmt();
+ curStmt = curStmt->GetPrevStmt();
assert(curStmt != nullptr);
- currentTree = curStmt->gtStmtExpr;
+ currentTree = curStmt->GetRootNode();
}
}
{
break;
}
- curr = curr->gtNext;
+ curr = curr->GetNextStmt();
} while (curr != nullptr);
return curr != nullptr;
}
{
// The new tree will now be the first one of the block.
block->bbStmtList = stmt;
- stmt->gtNext = firstStmt;
+ stmt->SetNextStmt(firstStmt);
// Are there any statements in the block?
if (firstStmt != nullptr)
{
// There is at least one statement already.
- Statement* lastStmt = firstStmt->gtPrev;
- noway_assert(lastStmt != nullptr && lastStmt->gtNext == nullptr);
+ Statement* lastStmt = firstStmt->GetPrevStmt();
+ noway_assert(lastStmt != nullptr && lastStmt->GetNextStmt() == nullptr);
// Insert the statement in front of the first one.
- firstStmt->gtPrev = stmt;
- stmt->gtPrev = lastStmt;
+ firstStmt->SetPrevStmt(stmt);
+ stmt->SetPrevStmt(lastStmt);
}
else
{
// The block was completely empty.
- stmt->gtPrev = stmt;
+ stmt->SetPrevStmt(stmt);
}
}
else
void Compiler::fgInsertStmtAtEnd(BasicBlock* block, Statement* stmt)
{
- assert(stmt->gtNext == nullptr); // We don't set it, and it needs to be this after the insert
+ assert(stmt->GetNextStmt() == nullptr); // We don't set it, and it needs to be this after the insert
Statement* firstStmt = block->firstStmt();
if (firstStmt != nullptr)
{
// There is at least one statement already.
- Statement* lastStmt = firstStmt->getPrevStmt();
+ Statement* lastStmt = firstStmt->GetPrevStmt();
noway_assert(lastStmt != nullptr && lastStmt->GetNextStmt() == nullptr);
// Append the statement after the last one.
- lastStmt->gtNext = stmt;
- stmt->gtPrev = lastStmt;
- firstStmt->gtPrev = stmt;
+ lastStmt->SetNextStmt(stmt);
+ stmt->SetPrevStmt(lastStmt);
+ firstStmt->SetPrevStmt(stmt);
}
else
{
// The block is completely empty.
block->bbStmtList = stmt;
- stmt->gtPrev = stmt;
+ stmt->SetPrevStmt(stmt);
}
}
Statement* firstStmt = block->firstStmt();
noway_assert(firstStmt != nullptr);
Statement* lastStmt = block->lastStmt();
- noway_assert(lastStmt != nullptr && lastStmt->gtNext == nullptr);
- Statement* insertionPoint = lastStmt->gtPrev;
+ noway_assert(lastStmt != nullptr && lastStmt->GetNextStmt() == nullptr);
+ Statement* insertionPoint = lastStmt->GetPrevStmt();
#if DEBUG
if (block->bbJumpKind == BBJ_COND)
{
- assert(lastStmt->gtStmtExpr->gtOper == GT_JTRUE);
+ assert(lastStmt->GetRootNode()->gtOper == GT_JTRUE);
}
else if (block->bbJumpKind == BBJ_RETURN)
{
- assert((lastStmt->gtStmtExpr->gtOper == GT_RETURN) || (lastStmt->gtStmtExpr->gtOper == GT_JMP) ||
+ assert((lastStmt->GetRootNode()->gtOper == GT_RETURN) || (lastStmt->GetRootNode()->gtOper == GT_JMP) ||
// BBJ_RETURN blocks in functions returning void do not get a GT_RETURN node if they
// have a .tail prefix (even if canTailCall returns false for these calls)
// code:Compiler::impImportBlockCode (search for the RET: label)
// Ditto for real tail calls (all code after them has been removed)
- ((lastStmt->gtStmtExpr->gtOper == GT_CALL) &&
- ((info.compRetType == TYP_VOID) || lastStmt->gtStmtExpr->AsCall()->IsTailCall())));
+ ((lastStmt->GetRootNode()->gtOper == GT_CALL) &&
+ ((info.compRetType == TYP_VOID) || lastStmt->GetRootNode()->AsCall()->IsTailCall())));
}
else
{
assert(block->bbJumpKind == BBJ_SWITCH);
- assert(lastStmt->gtStmtExpr->gtOper == GT_SWITCH);
+ assert(lastStmt->GetRootNode()->gtOper == GT_SWITCH);
}
#endif // DEBUG
// Append 'stmt' before 'lastStmt'.
- stmt->gtNext = lastStmt;
- lastStmt->gtPrev = stmt;
+ stmt->SetNextStmt(lastStmt);
+ lastStmt->SetPrevStmt(stmt);
if (firstStmt == lastStmt)
{
// There is only one stmt in the block.
block->bbStmtList = stmt;
- stmt->gtPrev = lastStmt;
+ stmt->SetPrevStmt(lastStmt);
}
else
{
// Append 'stmt' after 'insertionPoint'.
- noway_assert(insertionPoint != nullptr && (insertionPoint->gtNext == lastStmt));
- insertionPoint->gtNext = stmt;
- stmt->gtPrev = insertionPoint;
+ noway_assert(insertionPoint != nullptr && (insertionPoint->GetNextStmt() == lastStmt));
+ insertionPoint->SetNextStmt(stmt);
+ stmt->SetPrevStmt(insertionPoint);
}
}
else
assert(fgBlockContainsStatementBounded(block, insertionPoint));
assert(!fgBlockContainsStatementBounded(block, stmt, false));
- if (insertionPoint->gtNext == nullptr)
+ if (insertionPoint->GetNextStmt() == nullptr)
{
// Ok, we want to insert after the last statement of the block.
- stmt->gtNext = nullptr;
- stmt->gtPrev = insertionPoint;
+ stmt->SetNextStmt(nullptr);
+ stmt->SetPrevStmt(insertionPoint);
- insertionPoint->gtNext = stmt;
+ insertionPoint->SetNextStmt(stmt);
// Update the backward link of the first statement of the block
// to point to the new last statement.
- assert(block->bbStmtList->gtPrev == insertionPoint);
- block->bbStmtList->gtPrev = stmt;
+ assert(block->bbStmtList->GetPrevStmt() == insertionPoint);
+ block->bbStmtList->SetPrevStmt(stmt);
}
else
{
- stmt->gtNext = insertionPoint->gtNext;
- stmt->gtPrev = insertionPoint;
+ stmt->SetNextStmt(insertionPoint->GetNextStmt());
+ stmt->SetPrevStmt(insertionPoint);
- insertionPoint->gtNext->gtPrev = stmt;
- insertionPoint->gtNext = stmt;
+ insertionPoint->GetNextStmt()->SetPrevStmt(stmt);
+ insertionPoint->SetNextStmt(stmt);
}
}
Statement* first = block->firstStmt();
Statement* last = block->lastStmt();
- stmt->gtNext = first;
- stmt->gtPrev = last;
+ stmt->SetNextStmt(first);
+ stmt->SetPrevStmt(last);
block->bbStmtList = stmt;
- first->gtPrev = stmt;
+ first->SetPrevStmt(stmt);
}
else
{
- stmt->gtNext = insertionPoint;
- stmt->gtPrev = insertionPoint->gtPrev;
+ stmt->SetNextStmt(insertionPoint);
+ stmt->SetPrevStmt(insertionPoint->GetPrevStmt());
- insertionPoint->gtPrev->gtNext = stmt;
- insertionPoint->gtPrev = stmt;
+ insertionPoint->GetPrevStmt()->SetNextStmt(stmt);
+ insertionPoint->SetPrevStmt(stmt);
}
}
Statement* Compiler::fgInsertStmtListAfter(BasicBlock* block, Statement* stmtAfter, Statement* stmtList)
{
// Currently we can handle when stmtAfter and stmtList are non-NULL. This makes everything easy.
- noway_assert(stmtAfter);
- noway_assert(stmtList);
+ noway_assert(stmtAfter != nullptr);
+ noway_assert(stmtList != nullptr);
- Statement* stmtLast = stmtList->getPrevStmt(); // Last statement in a non-empty list, circular in the gtPrev list.
- noway_assert(stmtLast);
- noway_assert(stmtLast->gtNext == nullptr);
+ // Last statement in a non-empty list, circular in the GetPrevStmt() list.
+ Statement* stmtLast = stmtList->GetPrevStmt();
+ noway_assert(stmtLast != nullptr);
+ noway_assert(stmtLast->GetNextStmt() == nullptr);
Statement* stmtNext = stmtAfter->GetNextStmt();
if (stmtNext == nullptr)
{
- stmtAfter->gtNext = stmtList;
- stmtList->gtPrev = stmtAfter;
- block->bbStmtList->gtPrev = stmtLast;
+ stmtAfter->SetNextStmt(stmtList);
+ stmtList->SetPrevStmt(stmtAfter);
+ block->bbStmtList->SetPrevStmt(stmtLast);
}
else
{
- stmtAfter->gtNext = stmtList;
- stmtList->gtPrev = stmtAfter;
+ stmtAfter->SetNextStmt(stmtList);
+ stmtList->SetPrevStmt(stmtAfter);
- stmtLast->gtNext = stmtNext;
- stmtNext->gtPrev = stmtLast;
+ stmtLast->SetNextStmt(stmtNext);
+ stmtNext->SetPrevStmt(stmtLast);
}
- noway_assert(block->bbStmtList == nullptr || block->bbStmtList->gtPrev->gtNext == nullptr);
+ noway_assert(block->bbStmtList == nullptr || block->bbStmtList->GetPrevStmt()->GetNextStmt() == nullptr);
return stmtLast;
}
Statement* nextStmt = newStmt->GetNextStmt();
if (nextStmt != nullptr)
{
- // Is it possible for gtNext to be NULL?
- newStmt->gtStmtILoffsx = nextStmt->gtStmtILoffsx;
+ // Is it possible for gtNextStmt to be NULL?
+ newStmt->SetILOffsetX(nextStmt->GetILOffsetX());
}
}
{
// if I'm always jumping to the target, then this is not a condition that needs moving.
Statement* stmt = top->firstStmt();
- while (stmt->gtNext)
+ while (stmt->GetNextStmt() != nullptr)
{
stmt = stmt->GetNextStmt();
}
// That way, the potential (tail)call statement is always the last
// statement in the block.
// Otherwise, we will assert at the following line in fgMorphCall()
- // noway_assert(fgMorphStmt->gtNext == NULL);
+ // noway_assert(fgMorphStmt->GetNextStmt() == NULL);
)
{
// Neither .tailcall prefix, no tailcall stress. So move on.
}
#endif
- if (block->bbJumpKind == BBJ_RETURN && block->lastStmt()->gtStmtExpr->gtOper == GT_RETURN)
+ if (block->bbJumpKind == BBJ_RETURN && block->lastStmt()->GetRootNode()->gtOper == GT_RETURN)
{
- GenTree* retNode = block->lastStmt()->gtStmtExpr;
+ GenTree* retNode = block->lastStmt()->GetRootNode();
GenTree* retExpr = retNode->gtOp.gtOp1;
if (retExpr != nullptr)
returnBlock->bbJumpDest = constReturnBlock;
// Remove GT_RETURN since constReturnBlock returns the constant.
- assert(returnBlock->lastStmt()->gtStmtExpr->OperIs(GT_RETURN));
- assert(returnBlock->lastStmt()->gtStmtExpr->gtGetOp1()->IsIntegralConst());
+ assert(returnBlock->lastStmt()->GetRootNode()->OperIs(GT_RETURN));
+ assert(returnBlock->lastStmt()->GetRootNode()->gtGetOp1()->IsIntegralConst());
comp->fgRemoveStmt(returnBlock, returnBlock->lastStmt());
// Using 'returnBlock' as the insertion point for 'mergedReturnBlock'
return nullptr;
}
- GenTree* lastExpr = lastStmt->gtStmtExpr;
+ GenTree* lastExpr = lastStmt->GetRootNode();
if (!lastExpr->OperIs(GT_RETURN))
{
return nullptr;
for (Statement* stmt : block->Statements())
{
- if (stmt->gtStmtILoffsx != BAD_IL_OFFSET)
+ if (stmt->GetILOffsetX() != BAD_IL_OFFSET)
{
- return jitGetILoffs(stmt->gtStmtILoffsx);
+ return jitGetILoffs(stmt->GetILOffsetX());
}
}
newBlock->bbStmtList = stmt->GetNextStmt();
if (newBlock->bbStmtList != nullptr)
{
- newBlock->bbStmtList->gtPrev = curr->bbStmtList->gtPrev;
+ newBlock->bbStmtList->SetPrevStmt(curr->bbStmtList->GetPrevStmt());
}
- curr->bbStmtList->gtPrev = stmt;
- stmt->gtNext = nullptr;
+ curr->bbStmtList->SetPrevStmt(stmt);
+ stmt->SetNextStmt(nullptr);
// Update the IL offsets of the blocks to match the split.
#ifdef DEBUG
if (verbose &&
- stmt->gtStmtExpr->gtOper != GT_NOP) // Don't print if it is a GT_NOP. Too much noise from the inliner.
+ stmt->GetRootNode()->gtOper != GT_NOP) // Don't print if it is a GT_NOP. Too much noise from the inliner.
{
printf("\nRemoving statement ");
gtDispStmt(stmt);
}
#endif // DEBUG
- if (opts.compDbgCode && stmt->gtPrev != stmt && stmt->gtStmtILoffsx != BAD_IL_OFFSET)
+ if (opts.compDbgCode && stmt->GetPrevStmt() != stmt && stmt->GetILOffsetX() != BAD_IL_OFFSET)
{
/* TODO: For debuggable code, should we remove significant
statement boundaries. Or should we leave a GT_NO_OP in its place? */
Statement* firstStmt = block->firstStmt();
if (firstStmt == stmt) // Is it the first statement in the list?
{
- if (firstStmt->gtNext == nullptr)
+ if (firstStmt->GetNextStmt() == nullptr)
{
assert(firstStmt == block->lastStmt());
}
else
{
- block->bbStmtList = firstStmt->GetNextStmt();
- block->bbStmtList->gtPrev = firstStmt->gtPrev;
+ block->bbStmtList = firstStmt->GetNextStmt();
+ block->bbStmtList->SetPrevStmt(firstStmt->GetPrevStmt());
}
}
else if (stmt == block->lastStmt()) // Is it the last statement in the list?
{
- stmt->gtPrev->gtNext = nullptr;
- block->bbStmtList->gtPrev = stmt->gtPrev;
+ stmt->GetPrevStmt()->SetNextStmt(nullptr);
+ block->bbStmtList->SetPrevStmt(stmt->GetPrevStmt());
}
else // The statement is in the middle.
{
- assert(stmt->gtPrevStmt != nullptr && stmt->gtNext != nullptr);
+ assert(stmt->GetPrevStmt() != nullptr && stmt->GetNextStmt() != nullptr);
- Statement* prev = stmt->gtPrevStmt;
+ Statement* prev = stmt->GetPrevStmt();
- prev->gtNext = stmt->gtNext;
- stmt->gtNext->gtPrev = prev;
+ prev->SetNextStmt(stmt->GetNextStmt());
+ stmt->GetNextStmt()->SetPrevStmt(prev);
}
noway_assert(!optValnumCSE_phase);
return false;
}
- GenTree* tree = stmt->gtStmtExpr;
+ GenTree* tree = stmt->GetRootNode();
genTreeOps oper = tree->OperGet();
if (OperIsControlFlow(oper) || GenTree::OperIsHWIntrinsic(oper) || oper == GT_NO_OP)
// Does the second have any phis?
if (bNextFirst != nullptr && bNextFirst != bNextNonPhi1)
{
- Statement* bNextLast = bNextFirst->gtPrevStmt;
- assert(bNextLast->gtNext == nullptr);
+ Statement* bNextLast = bNextFirst->GetPrevStmt();
+ assert(bNextLast->GetNextStmt() == nullptr);
// Does "blk" have phis?
if (blkNonPhi1 != blkFirst)
Statement* blkLastPhi;
if (blkNonPhi1 != nullptr)
{
- blkLastPhi = blkNonPhi1->gtPrevStmt;
+ blkLastPhi = blkNonPhi1->GetPrevStmt();
}
else
{
- blkLastPhi = blkFirst->gtPrevStmt;
+ blkLastPhi = blkFirst->GetPrevStmt();
}
- blkLastPhi->gtNext = bNextFirst;
- bNextFirst->gtPrev = blkLastPhi;
+ blkLastPhi->SetNextStmt(bNextFirst);
+ bNextFirst->SetPrevStmt(blkLastPhi);
// Now, rest of "block" after last phi of "bNext".
Statement* bNextLastPhi = nullptr;
if (bNextNonPhi1 != nullptr)
{
- bNextLastPhi = bNextNonPhi1->gtPrevStmt;
+ bNextLastPhi = bNextNonPhi1->GetPrevStmt();
}
else
{
- bNextLastPhi = bNextFirst->gtPrevStmt;
+ bNextLastPhi = bNextFirst->GetPrevStmt();
}
- bNextLastPhi->gtNext = blkNonPhi1;
+ bNextLastPhi->SetNextStmt(blkNonPhi1);
if (blkNonPhi1 != nullptr)
{
- blkNonPhi1->gtPrev = bNextLastPhi;
+ blkNonPhi1->SetPrevStmt(bNextLastPhi);
}
else
{
// block has no non phis, so make the last statement be the last added phi.
- blkFirst->gtPrev = bNextLastPhi;
+ blkFirst->SetPrevStmt(bNextLastPhi);
}
// Now update the bbStmtList of "bNext".
bNext->bbStmtList = bNextNonPhi1;
if (bNextNonPhi1 != nullptr)
{
- bNextNonPhi1->gtPrev = bNextLast;
+ bNextNonPhi1->SetPrevStmt(bNextLast);
}
}
else
if (blkFirst != nullptr) // If "block" has no statements, fusion will work fine...
{
// First, bNextPhis at start of block.
- Statement* blkLast = blkFirst->gtPrevStmt;
+ Statement* blkLast = blkFirst->GetPrevStmt();
block->bbStmtList = bNextFirst;
// Now, rest of "block" (if it exists) after last phi of "bNext".
Statement* bNextLastPhi = nullptr;
if (bNextNonPhi1 != nullptr)
{
// There is a first non phi, so the last phi is before it.
- bNextLastPhi = bNextNonPhi1->gtPrevStmt;
+ bNextLastPhi = bNextNonPhi1->GetPrevStmt();
}
else
{
// All the statements are phi defns, so the last one is the prev of the first.
- bNextLastPhi = bNextFirst->gtPrevStmt;
+ bNextLastPhi = bNextFirst->GetPrevStmt();
}
- bNextFirst->gtPrev = blkLast;
- bNextLastPhi->gtNext = blkFirst;
- blkFirst->gtPrev = bNextLastPhi;
+ bNextFirst->SetPrevStmt(blkLast);
+ bNextLastPhi->SetNextStmt(blkFirst);
+ blkFirst->SetPrevStmt(bNextLastPhi);
// Now update the bbStmtList of "bNext"
bNext->bbStmtList = bNextNonPhi1;
if (bNextNonPhi1 != nullptr)
{
- bNextNonPhi1->gtPrev = bNextLast;
+ bNextNonPhi1->SetPrevStmt(bNextLast);
}
}
}
/* append list2 to list 1 */
- stmtLast1->gtNext = stmtList2;
- stmtList2->gtPrev = stmtLast1;
- stmtList1->gtPrev = stmtLast2;
+ stmtLast1->SetNextStmt(stmtList2);
+ stmtList2->SetPrevStmt(stmtLast1);
+ stmtList1->SetPrevStmt(stmtLast2);
}
}
else
{
if (firstNonPhi != nullptr)
{
- firstNonPhi->gtPrev = block->lastStmt();
+ firstNonPhi->SetPrevStmt(block->lastStmt());
}
block->bbStmtList = firstNonPhi;
}
else
{
Statement* test = block->lastStmt();
- GenTree* tree = test->gtStmtExpr;
+ GenTree* tree = test->GetRootNode();
noway_assert(tree->gtOper == GT_JTRUE);
}
else
{
- test->gtStmtExpr = sideEffList;
+ test->SetRootNode(sideEffList);
fgMorphBlockStmt(block, test DEBUGARG("fgRemoveConditionalJump"));
}
else
{
switchStmt = block->lastStmt();
- switchTree = switchStmt->gtStmtExpr;
+ switchTree = switchStmt->GetRootNode();
assert(switchTree->OperGet() == GT_SWITCH);
}
/* Replace the conditional statement with the list of side effects */
noway_assert(sideEffList->gtOper != GT_SWITCH);
- switchStmt->gtStmtExpr = sideEffList;
+ switchStmt->SetRootNode(sideEffList);
if (fgStmtListThreaded)
{
// is an assignment of a constant, arraylength, or a relop.
// This is because these statements produce information about values
// that would otherwise be lost at the upcoming merge point.
- GenTree* tree = lastStmt->gtStmtExpr;
+ GenTree* tree = lastStmt->GetRootNode();
if (tree->gtOper != GT_ASG)
{
return false;
return false;
}
- GenTree* tree = stmt->gtStmtExpr;
+ GenTree* tree = stmt->GetRootNode();
if (tree->gtOper != GT_JTRUE)
{
assert(stmt == target->lastStmt());
// Duplicate the target block at the end of this block
- GenTree* cloned = gtCloneExpr(stmt->gtStmtExpr);
+ GenTree* cloned = gtCloneExpr(stmt->GetRootNode());
noway_assert(cloned);
Statement* jmpStmt = gtNewStmt(cloned);
else
{
Statement* condStmt = block->lastStmt();
- GenTree* cond = condStmt->gtStmtExpr;
+ GenTree* cond = condStmt->GetRootNode();
noway_assert(cond->gtOper == GT_JTRUE);
/* check for SIDE_EFFECTS */
/* Replace the conditional statement with the list of side effects */
noway_assert(sideEffList->gtOper != GT_JTRUE);
- condStmt->gtStmtExpr = sideEffList;
+ condStmt->SetRootNode(sideEffList);
if (fgStmtListThreaded)
{
unsigned estDupCostSz = 0;
for (Statement* stmt : bDest->Statements())
{
- GenTree* expr = stmt->gtStmtExpr;
+ GenTree* expr = stmt->GetRootNode();
/* We call gtPrepareCost to measure the cost of duplicating this tree */
gtPrepareCost(expr);
if (newStmtList != nullptr)
{
- newLastStmt->gtNext = stmt;
+ newLastStmt->SetNextStmt(stmt);
}
else
{
newStmtList = stmt;
}
- stmt->gtPrev = newLastStmt;
- newLastStmt = stmt;
+ stmt->SetPrevStmt(newLastStmt);
+ newLastStmt = stmt;
}
// Get to the condition node from the statement tree.
- GenTree* condTree = newLastStmt->gtStmtExpr;
+ GenTree* condTree = newLastStmt->GetRootNode();
noway_assert(condTree->gtOper == GT_JTRUE);
// Set condTree to the operand to the GT_JTRUE.
if (lastStmt != nullptr)
{
- Statement* stmt = bJump->firstStmt();
- stmt->gtPrev = newLastStmt;
- lastStmt->gtNext = newStmtList;
- newStmtList->gtPrev = lastStmt;
+ Statement* stmt = bJump->firstStmt();
+ stmt->SetPrevStmt(newLastStmt);
+ lastStmt->SetNextStmt(newStmtList);
+ newStmtList->SetPrevStmt(lastStmt);
}
else
{
- bJump->bbStmtList = newStmtList;
- newStmtList->gtPrev = newLastStmt;
+ bJump->bbStmtList = newStmtList;
+ newStmtList->SetPrevStmt(newLastStmt);
}
//
/* Reverse the bPrev jump condition */
Statement* condTestStmt = bPrev->lastStmt();
- GenTree* condTest = condTestStmt->gtStmtExpr;
+ GenTree* condTest = condTestStmt->GetRootNode();
noway_assert(condTest->gtOper == GT_JTRUE);
condTest->gtOp.gtOp1 = gtReverseCond(condTest->gtOp.gtOp1);
fgTreeSeqLst = &list;
fgTreeSeqBeg = nullptr;
- fgSetTreeSeqHelper(stmt->gtStmtExpr, false);
+ fgSetTreeSeqHelper(stmt->GetRootNode(), false);
/* Record the address of the first node */
- stmt->gtStmtList = fgTreeSeqBeg;
+ stmt->SetTreeList(fgTreeSeqBeg);
#ifdef DEBUG
BAD_LIST:;
printf("\n");
- gtDispTree(stmt->gtStmtExpr);
+ gtDispTree(stmt->GetRootNode());
printf("\n");
for (GenTree* bad = &list; bad != nullptr; bad = bad->gtNext)
/* Are there any more trees in this basic block? */
- if (stmt->gtNext == nullptr)
+ if (stmt->GetNextStmt() == nullptr)
{
/* last statement in the tree list */
noway_assert(block->lastStmt() == stmt);
if (block->bbStmtList == stmt)
{
/* first statement in the list */
- assert(stmt->gtPrev->gtNext == nullptr);
+ assert(stmt->GetPrevStmt()->GetNextStmt() == nullptr);
}
else
{
- assert(stmt->gtPrev->gtNext == stmt);
+ assert(stmt->GetPrevStmt()->GetNextStmt() == stmt);
}
- assert(stmt->gtNext->gtPrev == stmt);
+ assert(stmt->GetNextStmt()->GetPrevStmt() == stmt);
#endif // DEBUG
}
}
assert(fgStmtListThreaded);
- noway_assert(stmt->gtStmtList);
+ noway_assert(stmt->GetTreeList());
// The first node's gtPrev must be nullptr (the gtPrev list is not circular).
// The last node's gtNext must be nullptr (the gtNext list is not circular). This is tested if the loop below
// terminates.
- assert(stmt->gtStmtList->gtPrev == nullptr);
+ assert(stmt->GetTreeList()->gtPrev == nullptr);
- for (GenTree* tree = stmt->gtStmtList; tree != nullptr; tree = tree->gtNext)
+ for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext)
{
if (tree->gtPrev)
{
}
else
{
- noway_assert(tree == stmt->gtStmtList);
+ noway_assert(tree == stmt->GetTreeList());
}
if (tree->gtNext)
}
else
{
- noway_assert(tree == stmt->gtStmtExpr);
+ noway_assert(tree == stmt->GetRootNode());
}
/* Cross-check gtPrev,gtNext with gtOp for simple trees */
noway_assert(tree->gtFlags & GTF_ORDER_SIDEEFF);
// The GT_CATCH_ARG has to be the first thing evaluated
noway_assert(stmt == block->FirstNonPhiDef());
- noway_assert(stmt->gtStmtList->gtOper == GT_CATCH_ARG);
+ noway_assert(stmt->GetTreeList()->gtOper == GT_CATCH_ARG);
// The root of the tree should have GTF_ORDER_SIDEEFF set
- noway_assert(stmt->gtStmtExpr->gtFlags & GTF_ORDER_SIDEEFF);
+ noway_assert(stmt->GetRootNode()->gtFlags & GTF_ORDER_SIDEEFF);
}
}
{
for (Statement* stmt : block->Statements())
{
- /* Verify that bbStmtList is threaded correctly */
- /* Note that for the statements list, the gtPrev list is circular. The gtNext list is not: gtNext of the
- * last statement in a block is nullptr. */
+ // Verify that bbStmtList is threaded correctly.
+ // Note that for the statements list, the GetPrevStmt() list is circular.
+ // The GetNextStmt() list is not: GetNextStmt() of the last statement in a block is nullptr.
- noway_assert(stmt->gtPrev);
+ noway_assert(stmt->GetPrevStmt() != nullptr);
if (stmt == block->bbStmtList)
{
- noway_assert(stmt->gtPrev->gtNext == nullptr);
+ noway_assert(stmt->GetPrevStmt()->GetNextStmt() == nullptr);
}
else
{
- noway_assert(stmt->gtPrev->gtNext == stmt);
+ noway_assert(stmt->GetPrevStmt()->GetNextStmt() == stmt);
}
- if (stmt->gtNext)
+ if (stmt->GetNextStmt() != nullptr)
{
- noway_assert(stmt->gtNext->gtPrev == stmt);
+ noway_assert(stmt->GetNextStmt()->GetPrevStmt() == stmt);
}
else
{
/* For each statement check that the exception flags are properly set */
- noway_assert(stmt->gtStmtExpr);
+ noway_assert(stmt->GetRootNode());
if (verbose && 0)
{
- gtDispTree(stmt->gtStmtExpr);
+ gtDispTree(stmt->GetRootNode());
}
- fgDebugCheckFlags(stmt->gtStmtExpr);
+ fgDebugCheckFlags(stmt->GetRootNode());
// Not only will this stress fgMorphBlockStmt(), but we also get all the checks
// done by fgMorphTree()
}
}
- // For each statement check that the nodes are threaded correctly - gtStmtList.
+ // For each statement check that the nodes are threaded correctly - m_treeList.
if (fgStmtListThreaded)
{
fgDebugCheckNodeLinks(block, stmt);
{
for (Statement* stmt : block->Statements())
{
- GenTree* root = stmt->gtStmtExpr;
+ GenTree* root = stmt->GetRootNode();
fgWalkTreePre(&root, UniquenessCheckWalker::MarkTreeId, &walker);
}
}
unsigned Compiler::fgCheckInlineDepthAndRecursion(InlineInfo* inlineInfo)
{
BYTE* candidateCode = inlineInfo->inlineCandidateInfo->methInfo.ILCode;
- InlineContext* inlineContext = inlineInfo->iciStmt->gtInlineContext;
+ InlineContext* inlineContext = inlineInfo->iciStmt->GetInlineContext();
InlineResult* inlineResult = inlineInfo->inlineResult;
// There should be a context for all candidates.
{
for (Statement* stmt : block->Statements())
{
- stmt->gtInlineContext = rootContext;
+ stmt->SetInlineContext(rootContext);
}
}
// In debug builds we want the inline tree to show all failed
// inlines. Some inlines may fail very early and never make it to
// candidate stage. So scan the tree looking for those early failures.
- fgWalkTreePre(&stmt->gtStmtExpr, fgFindNonInlineCandidate, stmt);
+ fgWalkTreePre(stmt->GetRootNodePointer(), fgFindNonInlineCandidate, stmt);
#endif
- GenTree* expr = stmt->gtStmtExpr;
+ GenTree* expr = stmt->GetRootNode();
// The importer ensures that all inline candidates are
// statement expressions. So see if we have a call.
//
// If so, remove the GT_NOP and continue
// on with the next statement.
- if (stmt->gtStmtExpr->IsNothingNode())
+ if (stmt->GetRootNode()->IsNothingNode())
{
fgRemoveStmt(block, stmt);
continue;
// possible further optimization, as the (now complete) GT_RET_EXPR
// replacement may have enabled optimizations by providing more
// specific types for trees or variables.
- fgWalkTree(&stmt->gtStmtExpr, fgUpdateInlineReturnExpressionPlaceHolder, fgLateDevirtualization,
+ fgWalkTree(stmt->GetRootNodePointer(), fgUpdateInlineReturnExpressionPlaceHolder, fgLateDevirtualization,
(void*)this);
// See if stmt is of the form GT_COMMA(call, nop)
if (expr->OperGet() == GT_COMMA && expr->gtOp.gtOp1->OperGet() == GT_CALL &&
expr->gtOp.gtOp2->OperGet() == GT_NOP)
{
- stmt->gtStmtExpr = expr->gtOp.gtOp1;
+ stmt->SetRootNode(expr->gtOp.gtOp1);
}
}
for (Statement* stmt : block->Statements())
{
// Call Compiler::fgDebugCheckInlineCandidates on each node
- fgWalkTreePre(&stmt->gtStmtExpr, fgDebugCheckInlineCandidates);
+ fgWalkTreePre(stmt->GetRootNodePointer(), fgDebugCheckInlineCandidates);
}
block = block->bbNext;
BasicBlock* block;
noway_assert(iciBlock->bbStmtList != nullptr);
- noway_assert(iciStmt->gtStmtExpr != nullptr);
- assert(iciStmt->gtStmtExpr == iciCall);
+ noway_assert(iciStmt->GetRootNode() != nullptr);
+ assert(iciStmt->GetRootNode() == iciCall);
noway_assert(iciCall->gtOper == GT_CALL);
#ifdef DEBUG
{
for (Statement* stmt : block->Statements())
{
- stmt->gtInlineContext = calleeContext;
+ stmt->SetInlineContext(calleeContext);
}
}
// This is the normal case where both blocks should contain at least one statement.
Statement* topBlock_Begin = topBlock->firstStmt();
noway_assert(topBlock_Begin != nullptr);
- Statement* topBlock_End = bottomBlock_Begin->gtPrevStmt;
+ Statement* topBlock_End = bottomBlock_Begin->GetPrevStmt();
noway_assert(topBlock_End != nullptr);
Statement* bottomBlock_End = topBlock->lastStmt();
noway_assert(bottomBlock_End != nullptr);
// Break the linkage between 2 blocks.
- topBlock_End->gtNext = nullptr;
+ topBlock_End->SetNextStmt(nullptr);
// Fix up all the pointers.
- topBlock->bbStmtList = topBlock_Begin;
- topBlock->bbStmtList->gtPrev = topBlock_End;
+ topBlock->bbStmtList = topBlock_Begin;
+ topBlock->bbStmtList->SetPrevStmt(topBlock_End);
- bottomBlock->bbStmtList = bottomBlock_Begin;
- bottomBlock->bbStmtList->gtPrev = bottomBlock_End;
+ bottomBlock->bbStmtList = bottomBlock_Begin;
+ bottomBlock->bbStmtList->SetPrevStmt(bottomBlock_End);
}
//
block->copyEHRegion(iciBlock);
block->bbFlags |= iciBlock->bbFlags & BBF_BACKWARD_JUMP;
- if (iciStmt->gtStmtILoffsx != BAD_IL_OFFSET)
+ if (iciStmt->GetILOffsetX() != BAD_IL_OFFSET)
{
- block->bbCodeOffs = jitGetILoffs(iciStmt->gtStmtILoffsx);
+ block->bbCodeOffs = jitGetILoffs(iciStmt->GetILOffsetX());
block->bbCodeOffsEnd = block->bbCodeOffs + 1; // TODO: is code size of 1 some magic number for inlining?
}
else
// Detach the GT_CALL node from the original statement by hanging a "nothing" node under it,
// so that fgMorphStmts can remove the statement once we return from here.
//
- iciStmt->gtStmtExpr = gtNewNothingNode();
+ iciStmt->SetRootNode(gtNewNothingNode());
}
//------------------------------------------------------------------------
{
BasicBlock* block = inlineInfo->iciBlock;
Statement* callStmt = inlineInfo->iciStmt;
- IL_OFFSETX callILOffset = callStmt->gtStmtILoffsx;
+ IL_OFFSETX callILOffset = callStmt->GetILOffsetX();
Statement* postStmt = callStmt->GetNextStmt();
Statement* afterStmt = callStmt; // afterStmt is the place where the new statements should be inserted after.
Statement* newStmt = nullptr;
}
// Update any newly added statements with the appropriate context.
- InlineContext* context = callStmt->gtInlineContext;
+ InlineContext* context = callStmt->GetInlineContext();
assert(context != nullptr);
for (Statement* addedStmt = callStmt->GetNextStmt(); addedStmt != postStmt; addedStmt = addedStmt->GetNextStmt())
{
- assert(addedStmt->gtInlineContext == nullptr);
- addedStmt->gtInlineContext = context;
+ assert(addedStmt->GetInlineContext() == nullptr);
+ addedStmt->SetInlineContext(context);
}
return afterStmt;
JITDUMP("fgInlineAppendStatements: nulling out gc ref inlinee locals.\n");
Statement* callStmt = inlineInfo->iciStmt;
- IL_OFFSETX callILOffset = callStmt->gtStmtILoffsx;
+ IL_OFFSETX callILOffset = callStmt->GetILOffsetX();
CORINFO_METHOD_INFO* InlineeMethodInfo = InlineeCompiler->info.compMethodInfo;
const unsigned lclCnt = InlineeMethodInfo->locals.numArgs;
InlLclVarInfo* lclVarInfo = inlineInfo->lclVarInfo;
for (Statement* stmt : firstBlock->Statements())
{
- GenTree* stmtExpr = stmt->gtStmtExpr;
+ GenTree* stmtExpr = stmt->GetRootNode();
if (stmtExpr->gtOper != GT_RETFILT)
{
if (block->bbJumpKind == BBJ_EHFINALLYRET)
{
Statement* finallyRet = block->lastStmt();
- GenTree* finallyRetExpr = finallyRet->gtStmtExpr;
+ GenTree* finallyRetExpr = finallyRet->GetRootNode();
assert(finallyRetExpr->gtOper == GT_RETFILT);
fgRemoveStmt(block, finallyRet);
block->bbJumpKind = BBJ_ALWAYS;
// since we're removing the enclosing handler.
for (Statement* stmt : block->Statements())
{
- GenTree* expr = stmt->gtStmtExpr;
+ GenTree* expr = stmt->GetRootNode();
if (expr->gtOper == GT_END_LFIN)
{
const unsigned nestLevel = expr->gtVal.gtVal1;
if (block->bbJumpKind == BBJ_EHFINALLYRET)
{
Statement* finallyRet = newBlock->lastStmt();
- GenTree* finallyRetExpr = finallyRet->gtStmtExpr;
+ GenTree* finallyRetExpr = finallyRet->GetRootNode();
assert(finallyRetExpr->gtOper == GT_RETFILT);
fgRemoveStmt(newBlock, finallyRet);
newBlock->bbJumpKind = BBJ_ALWAYS;
bool foundEndLFin = false;
for (Statement* stmt : continuation->Statements())
{
- GenTree* expr = stmt->gtStmtExpr;
+ GenTree* expr = stmt->GetRootNode();
if (expr->gtOper == GT_END_LFIN)
{
assert(!foundEndLFin);
{
for (Statement* stmt : block->Statements())
{
- fgWalkTreePre(&stmt->gtStmtExpr,
+ fgWalkTreePre(stmt->GetRootNodePointer(),
[](GenTree** slot, fgWalkData* data) -> Compiler::fgWalkResult {
(*reinterpret_cast<unsigned*>(data->pCallbackData))++;
return Compiler::WALK_CONTINUE;
static_assert_no_msg(sizeof(GenTreeDynBlk) <= TREE_NODE_SZ_LARGE); // *** large node
static_assert_no_msg(sizeof(GenTreeRetExpr) <= TREE_NODE_SZ_LARGE); // *** large node
static_assert_no_msg(sizeof(GenTreeILOffset) <= TREE_NODE_SZ_SMALL);
- static_assert_no_msg(sizeof(Statement) <= TREE_NODE_SZ_LARGE); // *** large node
static_assert_no_msg(sizeof(GenTreeClsVar) <= TREE_NODE_SZ_SMALL);
static_assert_no_msg(sizeof(GenTreeArgPlace) <= TREE_NODE_SZ_SMALL);
static_assert_no_msg(sizeof(GenTreePhiArg) <= TREE_NODE_SZ_SMALL);
{
for (Statement* stmt : block->Statements())
{
- fgWalkTreePre(&stmt->gtStmtExpr, visitor, pCallBackData);
+ fgWalkTreePre(stmt->GetRootNodePointer(), visitor, pCallBackData);
}
}
}
// under a struct assignment would not be considered for addressing modes.
if (compCurStmt != nullptr)
{
- GenTree* expr = compCurStmt->gtStmtExpr;
+ GenTree* expr = compCurStmt->GetRootNode();
if ((expr->OperGet() == GT_ASG) &&
((expr->gtGetOp1() == tree) || (expr->gtGetOp2() == tree)))
{
GenTree** treePtr = nullptr;
GenTree* treeParent = tree->gtGetParent(&treePtr);
- assert(treeParent != nullptr || tree == stmt->gtStmtExpr);
+ assert(treeParent != nullptr || tree == stmt->GetRootNode());
if (treePtr == nullptr)
{
// Replace the stmt expr and rebuild the linear order for "stmt".
assert(treeParent == nullptr);
assert(fgOrder != FGOrderLinear);
- stmt->gtStmtExpr = tree;
+ stmt->SetRootNode(tree);
fgSetStmtSeq(stmt);
}
else
{
// Update the linear oder start of "stmt" if treeFirstNode
// appears to have replaced the original first node.
- assert(treeFirstNode == stmt->gtStmtList);
- stmt->gtStmtList = fgGetFirstNode(replacementTree);
+ assert(treeFirstNode == stmt->GetTreeList());
+ stmt->SetTreeList(fgGetFirstNode(replacementTree));
}
if (treeNextNode != nullptr)
void Compiler::gtUpdateStmtSideEffects(Statement* stmt)
{
- fgWalkTree(&stmt->gtStmtExpr, fgUpdateSideEffectsPre, fgUpdateSideEffectsPost);
+ fgWalkTree(stmt->GetRootNodePointer(), fgUpdateSideEffectsPre, fgUpdateSideEffectsPost);
}
//------------------------------------------------------------------------
printf("%s ", msg);
}
printStmtID(stmt);
- IL_OFFSETX firstILOffsx = stmt->gtStmtILoffsx;
+ IL_OFFSETX firstILOffsx = stmt->GetILOffsetX();
printf(" (IL ");
if (firstILOffsx == BAD_IL_OFFSET)
{
}
printf("...");
- IL_OFFSET lastILOffs = stmt->gtStmtLastILoffs;
+ IL_OFFSET lastILOffs = stmt->GetLastILOffset();
if (lastILOffs == BAD_IL_OFFSET)
{
printf(" ???");
}
printf(")\n");
}
- gtDispTree(stmt->gtStmtExpr);
+ gtDispTree(stmt->GetRootNode());
}
//------------------------------------------------------------------------
asgStmt->GetID(), copyStmt->GetID());
// If we don't recognize the form of the assign, bail.
- GenTree* asg = asgStmt->gtStmtExpr;
+ GenTree* asg = asgStmt->GetRootNode();
if (asg->gtOper != GT_ASG)
{
JITDUMP(" bailing; unexpected assignment op %s\n", GenTree::OpName(asg->gtOper));
}
// If we don't recognize the form of the copy, bail.
- GenTree* copy = copyStmt->gtStmtExpr;
+ GenTree* copy = copyStmt->GetRootNode();
if (copy->gtOper != GT_ASG)
{
// GT_RET_EXPR is a tolerable temporary failure.
// value as the copy is fairly cheap and likely
// the optimizer can trim things down to just the
// minimal side effect parts.
- copyStmt->gtStmtExpr = copySrc;
+ copyStmt->SetRootNode(copySrc);
JITDUMP(" to scalar read via [%06u]\n", dspTreeID(copySrc));
}
else
// source struct; there's no need to read the
// entire thing, and no place to put it.
assert(copySrc->gtOper == GT_OBJ || copySrc->gtOper == GT_IND || copySrc->gtOper == GT_FIELD);
- copyStmt->gtStmtExpr = copySrc;
+ copyStmt->SetRootNode(copySrc);
if (options == BR_REMOVE_AND_NARROW || options == BR_REMOVE_AND_NARROW_WANT_TYPE_HANDLE)
{
const unsigned thisTmp = lvaGrabTemp(true DEBUGARG("Enum:HasFlag this temp"));
GenTree* thisAsg = gtNewTempAssign(thisTmp, thisVal);
Statement* thisAsgStmt = thisOp->AsBox()->gtCopyStmtWhenInlinedBoxValue;
- thisAsgStmt->gtStmtExpr = thisAsg;
- thisValOpt = gtNewLclvNode(thisTmp, type);
+ thisAsgStmt->SetRootNode(thisAsg);
+ thisValOpt = gtNewLclvNode(thisTmp, type);
}
if (flagVal->IsIntegralConst())
const unsigned flagTmp = lvaGrabTemp(true DEBUGARG("Enum:HasFlag flag temp"));
GenTree* flagAsg = gtNewTempAssign(flagTmp, flagVal);
Statement* flagAsgStmt = flagOp->AsBox()->gtCopyStmtWhenInlinedBoxValue;
- flagAsgStmt->gtStmtExpr = flagAsg;
- flagValOpt = gtNewLclvNode(flagTmp, type);
- flagValOptCopy = gtNewLclvNode(flagTmp, type);
+ flagAsgStmt->SetRootNode(flagAsg);
+ flagValOpt = gtNewLclvNode(flagTmp, type);
+ flagValOptCopy = gtNewLclvNode(flagTmp, type);
}
// Turn the call into (thisValTmp & flagTmp) == flagTmp.
{
FindLinkData data = {node, nullptr, nullptr};
- fgWalkResult result = fgWalkTreePre(&stmt->gtStmtExpr, gtFindLinkCB, &data);
+ fgWalkResult result = fgWalkTreePre(stmt->GetRootNodePointer(), gtFindLinkCB, &data);
if (result == WALK_ABORT)
{
struct Statement
{
public:
- GenTree* gtStmtExpr; // root of the expression tree
- GenTree* gtStmtList; // first node (for forward walks)
- InlineContext* gtInlineContext; // The inline context for this statement.
- IL_OFFSETX gtStmtILoffsx; // instr offset (if available)
-
+ Statement(GenTree* expr, IL_OFFSETX offset DEBUGARG(unsigned stmtID))
+ : m_rootNode(expr)
+ , m_treeList(nullptr)
+ , m_inlineContext(nullptr)
+ , m_ILOffsetX(offset)
#ifdef DEBUG
- IL_OFFSET gtStmtLastILoffs; // instr offset at end of stmt
-
-private:
- unsigned m_stmtID;
+ , m_lastILOffset(BAD_IL_OFFSET)
+ , m_stmtID(stmtID)
#endif
+ , m_next(nullptr)
+ , m_prev(nullptr)
+ , m_compilerAdded(false)
+ {
+ }
-public:
- __declspec(property(get = getPrevStmt)) Statement* gtPrevStmt;
+ GenTree* GetRootNode() const
+ {
+ return m_rootNode;
+ }
- Statement* gtNext;
- Statement* gtPrev;
+ GenTree** GetRootNodePointer()
+ {
+ return &m_rootNode;
+ }
- bool compilerAdded;
+ void SetRootNode(GenTree* treeRoot)
+ {
+ m_rootNode = treeRoot;
+ }
- Statement* GetNextStmt()
+ GenTree* GetTreeList() const
{
- if (gtNext == nullptr)
- {
- return nullptr;
- }
- else
- {
- return gtNext;
- }
+ return m_treeList;
}
- Statement* getPrevStmt()
+ void SetTreeList(GenTree* treeHead)
{
- if (gtPrev == nullptr)
- {
- return nullptr;
- }
- else
- {
- return gtPrev;
- }
+ m_treeList = treeHead;
}
- Statement(GenTree* expr, IL_OFFSETX offset DEBUGARG(unsigned stmtID))
- : gtStmtExpr(expr)
- , gtStmtList(nullptr)
- , gtInlineContext(nullptr)
- , gtStmtILoffsx(offset)
-#ifdef DEBUG
- , gtStmtLastILoffs(BAD_IL_OFFSET)
- , m_stmtID(stmtID)
-#endif
- , gtNext(nullptr)
- , gtPrev(nullptr)
- , compilerAdded(false)
+ InlineContext* GetInlineContext() const
{
+ return m_inlineContext;
}
- bool IsPhiDefnStmt()
+ void SetInlineContext(InlineContext* inlineContext)
{
- return gtStmtExpr->IsPhiDefn();
+ m_inlineContext = inlineContext;
}
- unsigned char GetCostSz() const
+ IL_OFFSETX GetILOffsetX() const
{
- return gtStmtExpr->GetCostSz();
+ return m_ILOffsetX;
}
- unsigned char GetCostEx() const
+ void SetILOffsetX(IL_OFFSETX offsetX)
{
- return gtStmtExpr->GetCostEx();
+ m_ILOffsetX = offsetX;
}
#ifdef DEBUG
+
+ IL_OFFSET GetLastILOffset() const
+ {
+ return m_lastILOffset;
+ }
+
+ void SetLastILOffset(IL_OFFSET lastILOffset)
+ {
+ m_lastILOffset = lastILOffset;
+ }
+
unsigned GetID() const
{
return m_stmtID;
}
+#endif // DEBUG
+
+ Statement* GetNextStmt() const
+ {
+ return m_next;
+ }
+
+ void SetNextStmt(Statement* nextStmt)
+ {
+ m_next = nextStmt;
+ }
+
+ Statement* GetPrevStmt() const
+ {
+ return m_prev;
+ }
+
+ void SetPrevStmt(Statement* prevStmt)
+ {
+ m_prev = prevStmt;
+ }
+
+ bool IsCompilerAdded() const
+ {
+ return m_compilerAdded;
+ }
+
+ void SetCompilerAdded()
+ {
+ m_compilerAdded = true;
+ }
+
+ bool IsPhiDefnStmt() const
+ {
+ return m_rootNode->IsPhiDefn();
+ }
+
+ unsigned char GetCostSz() const
+ {
+ return m_rootNode->GetCostSz();
+ }
+
+ unsigned char GetCostEx() const
+ {
+ return m_rootNode->GetCostEx();
+ }
+
+private:
+ // The root of the expression tree.
+ // Note: It will be the last node in evaluation order.
+ GenTree* m_rootNode;
+
+ // The tree list head (for forward walks in evaluation order).
+ // The value is `nullptr` until we have set the sequencing of the nodes.
+ GenTree* m_treeList;
+
+ InlineContext* m_inlineContext; // The inline context for this statement.
+
+ IL_OFFSETX m_ILOffsetX; // The instr offset (if available).
+
+#ifdef DEBUG
+ IL_OFFSET m_lastILOffset; // The instr offset at the end of this statement.
+ unsigned m_stmtID;
#endif
+
+ // The statement nodes are doubly-linked. The first statement node in a block points
+ // to the last node in the block via its `m_prev` link. Note that the last statement node
+ // does not point to the first: it's `m_next == nullptr`; that is, the list is not fully circular.
+ Statement* m_next;
+ Statement* m_prev;
+
+ bool m_compilerAdded; // Was the statement created by optimizer?
};
class StatementIterator
StatementIterator& operator++()
{
- m_stmt = m_stmt->gtNext;
+ m_stmt = m_stmt->GetNextStmt();
return *this;
}
for (Statement* stmt : block->Statements())
{
ReplaceShadowParamsVisitor replaceShadowParamsVisitor(this);
- replaceShadowParamsVisitor.WalkTree(&stmt->gtStmtExpr, nullptr);
+ replaceShadowParamsVisitor.WalkTree(stmt->GetRootNodePointer(), nullptr);
}
}
GTNODE(CALL , GenTreeCall ,0,(GTK_SPECIAL|GTK_NOCONTAIN))
GTNODE(FIELD_LIST , GenTreeFieldList ,0,GTK_SPECIAL) // List of fields of a struct, when passed as an argument
-//-----------------------------------------------------------------------------
-// Statement operator nodes:
-//-----------------------------------------------------------------------------
-GTNODE(STMT , Statement ,0,(GTK_SPECIAL|GTK_NOVALUE))// top-level list nodes in GetFirstLIRNode()
-
GTNODE(RETURN , GenTreeOp ,0,(GTK_UNOP|GTK_NOVALUE)) // return from current function
GTNODE(SWITCH , GenTreeOp ,0,(GTK_UNOP|GTK_NOVALUE)) // switch
{
/* Make the list circular, so that we can easily walk it backwards */
- firstStmt->gtPrev = lastStmt;
+ firstStmt->SetPrevStmt(lastStmt);
/* Store the tree list in the basic block */
#ifdef DEBUG
if (impLastILoffsStmt != nullptr)
{
- impLastILoffsStmt->gtStmtLastILoffs = compIsForInlining() ? BAD_IL_OFFSET : impCurOpcOffs;
- impLastILoffsStmt = nullptr;
+ impLastILoffsStmt->SetLastILOffset(compIsForInlining() ? BAD_IL_OFFSET : impCurOpcOffs);
+ impLastILoffsStmt = nullptr;
}
#endif
impStmtList = impLastStmt = nullptr;
return;
}
- GenTree* tree = stmt->gtStmtExpr;
+ GenTree* tree = stmt->GetRootNode();
// Calls can only be appended if there are no GTF_GLOB_EFFECT on the stack
/* If the statement being appended has any side-effects, check the stack
to see if anything needs to be spilled to preserve correct ordering. */
- GenTree* expr = stmt->gtStmtExpr;
+ GenTree* expr = stmt->GetRootNode();
unsigned flags = expr->gtFlags & GTF_GLOB_EFFECT;
// Assignment to (unaliased) locals don't count as a side-effect as
/* Once we set impCurStmtOffs in an appended tree, we are ready to
report the following offsets. So reset impCurStmtOffs */
- if (impLastStmt->gtStmtILoffsx == impCurStmtOffs)
+ if (impLastStmt->GetILOffsetX() == impCurStmtOffs)
{
impCurStmtOffsSet(BAD_IL_OFFSET);
}
else
{
// Append the expression statement to the existing list.
- impLastStmt->gtNext = stmt;
- stmt->gtPrev = impLastStmt;
+ impLastStmt->SetNextStmt(stmt);
+ stmt->SetPrevStmt(impLastStmt);
}
impLastStmt = stmt;
}
assert(impLastStmt != nullptr);
Statement* stmt = impLastStmt;
- impLastStmt = impLastStmt->gtPrevStmt;
+ impLastStmt = impLastStmt->GetPrevStmt();
if (impLastStmt == nullptr)
{
impStmtList = nullptr;
}
else
{
- Statement* stmtPrev = stmtBefore->getPrevStmt();
- stmt->gtPrev = stmtPrev;
- stmtPrev->gtNext = stmt;
+ Statement* stmtPrev = stmtBefore->GetPrevStmt();
+ stmt->SetPrevStmt(stmtPrev);
+ stmtPrev->SetNextStmt(stmt);
}
- stmt->gtNext = stmtBefore;
- stmtBefore->gtPrev = stmt;
+ stmt->SetNextStmt(stmtBefore);
+ stmtBefore->SetPrevStmt(stmt);
}
/*****************************************************************************
if (stmt != nullptr)
{
- GenTree* tree = stmt->gtStmtExpr;
+ GenTree* tree = stmt->GetRootNode();
assert(tree != nullptr);
if ((tree->gtOper == GT_ASG) && (tree->gtOp.gtOp1->gtOper == GT_LCL_VAR) &&
if (compIsForInlining())
{
Statement* callStmt = impInlineInfo->iciStmt;
- impCurStmtOffs = callStmt->gtStmtILoffsx;
+ impCurStmtOffs = callStmt->GetILOffsetX();
}
else
{
*
* Remember the instr offset for the statements
*
- * When we do impAppendTree(tree), we can't set tree->gtStmtLastILoffs to
- * impCurOpcOffs, if the append was done because of a partial stack spill,
+ * When we do impAppendTree(tree), we can't set stmt->SetLastILOffset(impCurOpcOffs),
+ * if the append was done because of a partial stack spill,
* as some of the trees corresponding to code up to impCurOpcOffs might
* still be sitting on the stack.
- * So we delay marking of gtStmtLastILoffs until impNoteLastILoffs().
+ * So we delay calling of SetLastILOffset() until impNoteLastILoffs().
* This should be called when an opcode finally/explicitly causes
* impAppendTree(tree) to be called (as opposed to being called because of
* a spill caused by the opcode)
assert(impLastStmt);
- impLastStmt->gtStmtLastILoffs = compIsForInlining() ? BAD_IL_OFFSET : impCurOpcOffs;
+ impLastStmt->SetLastILOffset(compIsForInlining() ? BAD_IL_OFFSET : impCurOpcOffs);
}
else
{
- impLastILoffsStmt->gtStmtLastILoffs = compIsForInlining() ? BAD_IL_OFFSET : impCurOpcOffs;
- impLastILoffsStmt = nullptr;
+ impLastILoffsStmt->SetLastILOffset(compIsForInlining() ? BAD_IL_OFFSET : impCurOpcOffs);
+ impLastILoffsStmt = nullptr;
}
}
// We start by looking at the last statement, making sure it's an assignment, and
// that the target of the assignment is the array passed to InitializeArray.
//
- GenTree* arrayAssignment = impLastStmt->gtStmtExpr;
+ GenTree* arrayAssignment = impLastStmt->GetRootNode();
if ((arrayAssignment->gtOper != GT_ASG) || (arrayAssignment->gtOp.gtOp1->gtOper != GT_LCL_VAR) ||
(arrayLocalNode->gtOper != GT_LCL_VAR) ||
(arrayAssignment->gtOp.gtOp1->gtLclVarCommon.GetLclNum() != arrayLocalNode->gtLclVarCommon.GetLclNum()))
if (endCatches)
{
- lastStmt = gtNewStmt(endCatches);
- endLFinStmt->gtNext = lastStmt;
- lastStmt->gtPrev = endLFinStmt;
+ lastStmt = gtNewStmt(endCatches);
+ endLFinStmt->SetNextStmt(lastStmt);
+ lastStmt->SetPrevStmt(endLFinStmt);
}
else
{
if (endCatches)
{
- lastStmt = gtNewStmt(endCatches);
- endLFinStmt->gtNext = lastStmt;
- lastStmt->gtPrev = endLFinStmt;
+ lastStmt = gtNewStmt(endCatches);
+ endLFinStmt->SetNextStmt(lastStmt);
+ lastStmt->SetPrevStmt(endLFinStmt);
}
else
{
#ifndef _TARGET_64BIT_
// In UWP6.0 and beyond (post-.NET Core 2.0), we decided to let this cast from int to long be
// generated for ARM as well as x86, so the following IR will be accepted:
- // * STMT void
- // | /--* CNS_INT int 2
- // \--* ASG long
- // \--* CLS_VAR long
+ // STMTx (IL 0x... ???)
+ // * ASG long
+ // +--* CLS_VAR long
+ // \--* CNS_INT int 2
if ((op1->TypeGet() != op2->TypeGet()) && op2->OperIsConst() && varTypeIsIntOrI(op2->TypeGet()) &&
varTypeIsLong(op1->TypeGet()))
addStmt = impExtractLastStmt();
- assert(addStmt->gtStmtExpr->gtOper == GT_JTRUE);
+ assert(addStmt->GetRootNode()->gtOper == GT_JTRUE);
/* Note if the next block has more than one ancestor */
unsigned jmpCnt;
addStmt = impExtractLastStmt();
- assert(addStmt->gtStmtExpr->gtOper == GT_SWITCH);
+ assert(addStmt->GetRootNode()->gtOper == GT_SWITCH);
jmpCnt = block->bbJumpSwt->bbsCount;
jmpTab = block->bbJumpSwt->bbsDstTab;
are spilling to the temps already used by a previous block),
we need to spill addStmt */
- if (addStmt != nullptr && !newTemps && gtHasRef(addStmt->gtStmtExpr, tempNum, false))
+ if (addStmt != nullptr && !newTemps && gtHasRef(addStmt->GetRootNode(), tempNum, false))
{
- GenTree* addTree = addStmt->gtStmtExpr;
+ GenTree* addTree = addStmt->GetRootNode();
if (addTree->gtOper == GT_JTRUE)
{
for (Statement* stmt : StatementList(impStmtList))
{
- GenTree* expr = stmt->gtStmtExpr;
+ GenTree* expr = stmt->GetRootNode();
if (GTF_GLOBALLY_VISIBLE_SIDE_EFFECTS(expr->gtFlags))
{
return FALSE;
//
bool ContainsFatCalli(Statement* stmt)
{
- GenTree* fatPointerCandidate = stmt->gtStmtExpr;
+ GenTree* fatPointerCandidate = stmt->GetRootNode();
if (fatPointerCandidate->OperIs(GT_ASG))
{
fatPointerCandidate = fatPointerCandidate->gtGetOp2();
// calls are hoisted to top level ... (we hope)
bool ContainsGuardedDevirtualizationCandidate(Statement* stmt)
{
- GenTree* candidate = stmt->gtStmtExpr;
+ GenTree* candidate = stmt->GetRootNode();
return candidate->IsCall() && candidate->AsCall()->IsGuardedDevirtualizationCandidate();
}
FatPointerCallTransformer(Compiler* compiler, BasicBlock* block, Statement* stmt)
: Transformer(compiler, block, stmt)
{
- doesReturnValue = stmt->gtStmtExpr->OperIs(GT_ASG);
+ doesReturnValue = stmt->GetRootNode()->OperIs(GT_ASG);
origCall = GetCall(stmt);
fptrAddress = origCall->gtCallAddr;
pointerType = fptrAddress->TypeGet();
// call tree node pointer.
virtual GenTreeCall* GetCall(Statement* callStmt)
{
- GenTree* tree = callStmt->gtStmtExpr;
+ GenTree* tree = callStmt->GetRootNode();
GenTreeCall* call = nullptr;
if (doesReturnValue)
{
GenTree* zero = new (compiler, GT_CNS_INT) GenTreeIntCon(TYP_I_IMPL, 0);
GenTree* fatPointerCmp = compiler->gtNewOperNode(GT_NE, TYP_INT, fatPointerAnd, zero);
GenTree* jmpTree = compiler->gtNewOperNode(GT_JTRUE, TYP_VOID, fatPointerCmp);
- Statement* jmpStmt = compiler->fgNewStmtFromTree(jmpTree, stmt->gtStmtILoffsx);
+ Statement* jmpStmt = compiler->fgNewStmtFromTree(jmpTree, stmt->GetILOffsetX());
compiler->fgInsertStmtAtEnd(checkBlock, jmpStmt);
}
Statement* CreateFatCallStmt(GenTree* actualCallAddress, GenTree* hiddenArgument)
{
Statement* fatStmt = compiler->gtCloneStmt(stmt);
- GenTree* fatTree = fatStmt->gtStmtExpr;
+ GenTree* fatTree = fatStmt->GetRootNode();
GenTreeCall* fatCall = GetCall(fatStmt);
fatCall->gtCallAddr = actualCallAddress;
AddHiddenArgument(fatCall, hiddenArgument);
// call tree node pointer.
virtual GenTreeCall* GetCall(Statement* callStmt)
{
- GenTree* tree = callStmt->gtStmtExpr;
+ GenTree* tree = callStmt->GetRootNode();
assert(tree->IsCall());
GenTreeCall* call = tree->AsCall();
return call;
const unsigned thisTempNum = compiler->lvaGrabTemp(true DEBUGARG("guarded devirt this temp"));
// lvaSetClass(thisTempNum, ...);
GenTree* asgTree = compiler->gtNewTempAssign(thisTempNum, thisTree);
- Statement* asgStmt = compiler->fgNewStmtFromTree(asgTree, stmt->gtStmtILoffsx);
+ Statement* asgStmt = compiler->fgNewStmtFromTree(asgTree, stmt->GetILOffsetX());
compiler->fgInsertStmtAtEnd(checkBlock, asgStmt);
thisTree = compiler->gtNewLclvNode(thisTempNum, TYP_REF);
// Compare and jump to else (which does the indirect call) if NOT equal
GenTree* methodTableCompare = compiler->gtNewOperNode(GT_NE, TYP_INT, targetMethodTable, methodTable);
GenTree* jmpTree = compiler->gtNewOperNode(GT_JTRUE, TYP_VOID, methodTableCompare);
- Statement* jmpStmt = compiler->fgNewStmtFromTree(jmpTree, stmt->gtStmtILoffsx);
+ Statement* jmpStmt = compiler->fgNewStmtFromTree(jmpTree, stmt->GetILOffsetX());
compiler->fgInsertStmtAtEnd(checkBlock, jmpStmt);
}
if (returnTemp != BAD_VAR_NUM)
{
- GenTree* assign = compiler->gtNewTempAssign(returnTemp, call);
- newStmt->gtStmtExpr = assign;
+ GenTree* assign = compiler->gtNewTempAssign(returnTemp, call);
+ newStmt->SetRootNode(assign);
}
// For stub calls, restore the stub address. For everything else,
compiler->fgInsertStmtAtEnd(elseBlock, newStmt);
// Set the original statement to a nop.
- stmt->gtStmtExpr = compiler->gtNewNothingNode();
+ stmt->SetRootNode(compiler->gtNewNothingNode());
}
private:
{
for (Statement* stmt : block->Statements())
{
- fgWalkTreePre(&stmt->gtStmtExpr, fgDebugCheckForTransformableIndirectCalls);
+ fgWalkTreePre(stmt->GetRootNodePointer(), fgDebugCheckForTransformableIndirectCalls);
}
}
}
// Pass along some optional information to the policy.
if (stmt != nullptr)
{
- m_InlineContext = stmt->gtInlineContext;
+ m_InlineContext = stmt->GetInlineContext();
m_Policy->NoteContext(m_InlineContext);
#if defined(DEBUG) || defined(INLINE_DATA)
m_Policy->NoteOffset(call->gtRawILOffset);
#else
- m_Policy->NoteOffset(stmt->gtStmtILoffsx);
+ m_Policy->NoteOffset(stmt->GetILOffsetX());
#endif // defined(DEBUG) || defined(INLINE_DATA)
}
Statement* stmt = inlineInfo->iciStmt;
BYTE* calleeIL = inlineInfo->inlineCandidateInfo->methInfo.ILCode;
unsigned calleeILSize = inlineInfo->inlineCandidateInfo->methInfo.ILCodeSize;
- InlineContext* parentContext = stmt->gtInlineContext;
+ InlineContext* parentContext = stmt->GetInlineContext();
GenTreeCall* originalCall = inlineInfo->inlineResult->GetCall();
noway_assert(parentContext != nullptr);
calleeContext->m_Sibling = parentContext->m_Child;
parentContext->m_Child = calleeContext;
calleeContext->m_Child = nullptr;
- calleeContext->m_Offset = stmt->gtStmtILoffsx;
+ calleeContext->m_Offset = stmt->GetILOffsetX();
calleeContext->m_Observation = inlineInfo->inlineResult->GetObservation();
calleeContext->m_Success = true;
calleeContext->m_Devirtualized = originalCall->IsDevirtualized();
{
// Check for a parent context first. We should now have a parent
// context for all statements.
- InlineContext* parentContext = stmt->gtInlineContext;
+ InlineContext* parentContext = stmt->GetInlineContext();
assert(parentContext != nullptr);
InlineContext* failedContext = new (m_Compiler, CMK_Inlining) InlineContext(this);
GenTreeCall* originalCall = inlineResult->GetCall();
failedContext->m_Sibling = parentContext->m_Child;
parentContext->m_Child = failedContext;
failedContext->m_Child = nullptr;
- failedContext->m_Offset = stmt->gtStmtILoffsx;
+ failedContext->m_Offset = stmt->GetILOffsetX();
failedContext->m_Observation = inlineResult->GetObservation();
failedContext->m_Callee = inlineResult->GetCallee();
failedContext->m_Success = false;
{
MarkLocalVarsVisitor visitor(this, block, stmt, isRecompute);
DISPSTMT(stmt);
- visitor.WalkTree(&stmt->gtStmtExpr, nullptr);
+ visitor.WalkTree(stmt->GetRootNodePointer(), nullptr);
}
}
for (Statement* stmt : StatementList(block->FirstNonPhiDef()))
{
compCurStmt = stmt;
- for (GenTree* node = stmt->gtStmtList; node != nullptr; node = node->gtNext)
+ for (GenTree* node = stmt->GetTreeList(); node != nullptr; node = node->gtNext)
{
fgPerNodeLocalVarLiveness(node);
}
VARSET_VALARG_TP volatileVars,
bool* pStmtInfoDirty DEBUGARG(bool* treeModf))
{
- GenTree* tree;
-
// Don't kill vars in scope
VARSET_TP keepAliveVars(VarSetOps::Union(this, volatileVars, compCurBB->bbScope));
noway_assert(VarSetOps::IsSubset(this, keepAliveVars, life));
- noway_assert(endNode || (startNode == compCurStmt->gtStmtExpr));
+ noway_assert(endNode || (startNode == compCurStmt->GetRootNode()));
// NOTE: Live variable analysis will not work if you try
// to use the result of an assignment node directly!
- for (tree = startNode; tree != endNode; tree = tree->gtPrev)
+ for (GenTree* tree = startNode; tree != endNode; tree = tree->gtPrev)
{
AGAIN:
assert(tree->OperGet() != GT_QMARK);
{
// This is a "NORMAL" statement with the assignment node hanging from the statement.
- noway_assert(compCurStmt->gtStmtExpr == asgNode);
+ noway_assert(compCurStmt->GetRootNode() == asgNode);
JITDUMP("top level assign\n");
if (sideEffList != nullptr)
// Replace the assignment statement with the list of side effects
- *pTree = compCurStmt->gtStmtExpr = sideEffList;
+ *pTree = sideEffList;
+ compCurStmt->SetRootNode(sideEffList);
#ifdef DEBUG
*treeModf = true;
#endif // DEBUG
noway_assert(nextStmt != nullptr);
compCurStmt = nextStmt;
- nextStmt = nextStmt->getPrevStmt();
+ nextStmt = nextStmt->GetPrevStmt();
/* Compute the liveness for each tree node in the statement */
bool stmtInfoDirty = false;
- fgComputeLife(life, compCurStmt->gtStmtExpr, nullptr, volatileVars, &stmtInfoDirty DEBUGARG(&treeModf));
+ fgComputeLife(life, compCurStmt->GetRootNode(), nullptr, volatileVars,
+ &stmtInfoDirty DEBUGARG(&treeModf));
if (stmtInfoDirty)
{
if (verbose && treeModf)
{
printf("\nfgComputeLife modified tree:\n");
- gtDispTree(compCurStmt->gtStmtExpr);
+ gtDispTree(compCurStmt->GetRootNode());
printf("\n");
}
#endif // DEBUG
// hanging a "nothing" node to it. Later the "nothing" node will be removed
// and the original GT_CALL tree will be picked up by the GT_RET_EXPR node.
- noway_assert(fgMorphStmt->gtStmtExpr == call);
- fgMorphStmt->gtStmtExpr = gtNewNothingNode();
+ noway_assert(fgMorphStmt->GetRootNode() == call);
+ fgMorphStmt->SetRootNode(gtNewNothingNode());
}
}
}
call->fgArgInfo = nullptr;
}
- GenTree* stmtExpr = fgMorphStmt->gtStmtExpr;
+ GenTree* stmtExpr = fgMorphStmt->GetRootNode();
#ifdef DEBUG
// Tail call needs to be in one of the following IR forms
fgRemoveStmt(compCurBB, stmtToRemove);
}
- fgMorphStmt->gtStmtExpr = call;
+ fgMorphStmt->SetRootNode(call);
// Tail call via helper: The VM can't use return address hijacking if we're
// not going to return and the helper doesn't have enough info to safely poll,
{
assert(recursiveTailCall->IsTailCallConvertibleToLoop());
Statement* lastStmt = block->lastStmt();
- assert(recursiveTailCall == lastStmt->gtStmtExpr);
+ assert(recursiveTailCall == lastStmt->GetRootNode());
// Transform recursive tail call into a loop.
Statement* earlyArgInsertionPoint = lastStmt;
- IL_OFFSETX callILOffset = lastStmt->gtStmtILoffsx;
+ IL_OFFSETX callILOffset = lastStmt->GetILOffsetX();
// Hoist arg setup statement for the 'this' argument.
GenTreeCall::Use* thisArg = recursiveTailCall->gtCallThisArg;
assg = fgMorphTree(assg);
// Create the assignment statement and insert it before the current statement.
- Statement* assgStmt = gtNewStmt(assg, compCurStmt->gtStmtILoffsx);
+ Statement* assgStmt = gtNewStmt(assg, compCurStmt->GetILOffsetX());
fgInsertStmtBefore(compCurBB, compCurStmt, assgStmt);
// Return the temp.
|| call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_READYTORUN_VIRTUAL_FUNC_PTR)
#endif
) &&
- (call == fgMorphStmt->gtStmtExpr))
+ (call == fgMorphStmt->GetRootNode()))
{
// This is call to CORINFO_HELP_VIRTUAL_FUNC_PTR with ignored result.
// Transform it into a null check.
if (block->bbJumpKind == BBJ_COND)
{
- noway_assert(block->bbStmtList != nullptr && block->bbStmtList->gtPrev != nullptr);
+ noway_assert(block->bbStmtList != nullptr && block->bbStmtList->GetPrevStmt() != nullptr);
Statement* lastStmt = block->lastStmt();
- noway_assert(lastStmt->gtNext == nullptr);
+ noway_assert(lastStmt->GetNextStmt() == nullptr);
- if (lastStmt->gtStmtExpr->gtOper == GT_CALL)
+ if (lastStmt->GetRootNode()->gtOper == GT_CALL)
{
noway_assert(fgRemoveRestOfBlock);
goto DONE_COND;
}
- noway_assert(lastStmt->gtStmtExpr->gtOper == GT_JTRUE);
+ noway_assert(lastStmt->GetRootNode()->gtOper == GT_JTRUE);
/* Did we fold the conditional */
- noway_assert(lastStmt->gtStmtExpr->gtOp.gtOp1);
+ noway_assert(lastStmt->GetRootNode()->gtOp.gtOp1);
GenTree* cond;
- cond = lastStmt->gtStmtExpr->gtOp.gtOp1;
+ cond = lastStmt->GetRootNode()->gtOp.gtOp1;
if (cond->OperKind() & GTK_CONST)
{
}
else if (block->bbJumpKind == BBJ_SWITCH)
{
- noway_assert(block->bbStmtList != nullptr && block->bbStmtList->gtPrev != nullptr);
+ noway_assert(block->bbStmtList != nullptr && block->bbStmtList->GetPrevStmt() != nullptr);
Statement* lastStmt = block->lastStmt();
- noway_assert(lastStmt->gtNext == nullptr);
+ noway_assert(lastStmt->GetNextStmt() == nullptr);
- if (lastStmt->gtStmtExpr->gtOper == GT_CALL)
+ if (lastStmt->GetRootNode()->gtOper == GT_CALL)
{
noway_assert(fgRemoveRestOfBlock);
goto DONE_SWITCH;
}
- noway_assert(lastStmt->gtStmtExpr->gtOper == GT_SWITCH);
+ noway_assert(lastStmt->GetRootNode()->gtOper == GT_SWITCH);
/* Did we fold the conditional */
- noway_assert(lastStmt->gtStmtExpr->gtOp.gtOp1);
+ noway_assert(lastStmt->GetRootNode()->gtOp.gtOp1);
GenTree* cond;
- cond = lastStmt->gtStmtExpr->gtOp.gtOp1;
+ cond = lastStmt->GetRootNode()->gtOp.gtOp1;
if (cond->OperKind() & GTK_CONST)
{
compCurBB = block;
compCurStmt = stmt;
- GenTree* morph = fgMorphTree(stmt->gtStmtExpr);
+ GenTree* morph = fgMorphTree(stmt->GetRootNode());
// Bug 1106830 - During the CSE phase we can't just remove
// morph->gtOp.gtOp2 as it could contain CSE expressions.
}
}
- stmt->gtStmtExpr = morph;
+ stmt->SetRootNode(morph);
// Can the entire tree be removed?
bool removedStmt = false;
if (fgRemoveRestOfBlock)
{
// Remove the rest of the stmts in the block
- for (Statement* removeStmt : StatementList(stmt->gtNext))
+ for (Statement* removeStmt : StatementList(stmt->GetNextStmt()))
{
fgRemoveStmt(block, removeStmt);
}
fgCurrentlyInUseArgTemps = hashBv::Create(this);
- Statement* stmt = block->firstStmt();
- GenTree* prev = nullptr;
- for (; stmt != nullptr; prev = stmt->gtStmtExpr, stmt = stmt->GetNextStmt())
+ for (Statement* stmt : block->Statements())
{
if (fgRemoveRestOfBlock)
{
continue;
}
#ifdef FEATURE_SIMD
- if (opts.OptimizationEnabled() && stmt->gtStmtExpr->TypeGet() == TYP_FLOAT &&
- stmt->gtStmtExpr->OperGet() == GT_ASG)
+ if (opts.OptimizationEnabled() && stmt->GetRootNode()->TypeGet() == TYP_FLOAT &&
+ stmt->GetRootNode()->OperGet() == GT_ASG)
{
fgMorphCombineSIMDFieldAssignments(block, stmt);
}
#endif
- fgMorphStmt = stmt;
- compCurStmt = stmt;
- GenTree* tree = stmt->gtStmtExpr;
+ fgMorphStmt = stmt;
+ compCurStmt = stmt;
+ GenTree* oldTree = stmt->GetRootNode();
#ifdef DEBUG
- unsigned oldHash = verbose ? gtHashValue(tree) : DUMMY_INIT(~0);
+ unsigned oldHash = verbose ? gtHashValue(oldTree) : DUMMY_INIT(~0);
if (verbose)
{
printf("\nfgMorphTree " FMT_BB ", " FMT_STMT " (before)\n", block->bbNum, stmt->GetID());
- gtDispTree(tree);
+ gtDispTree(oldTree);
}
#endif
/* Morph this statement tree */
- GenTree* morph = fgMorphTree(tree);
+ GenTree* morphedTree = fgMorphTree(oldTree);
// mark any outgoing arg temps as free so we can reuse them in the next statement.
// Has fgMorphStmt been sneakily changed ?
- if (stmt->gtStmtExpr != tree)
+ if (stmt->GetRootNode() != oldTree)
{
- /* This must be tailcall. Ignore 'morph' and carry on with
+ /* This must be tailcall. Ignore 'morphedTree' and carry on with
the tail-call node */
- morph = stmt->gtStmtExpr;
+ morphedTree = stmt->GetRootNode();
noway_assert(compTailCallUsed);
- noway_assert((morph->gtOper == GT_CALL) && morph->AsCall()->IsTailCall());
+ noway_assert((morphedTree->gtOper == GT_CALL) && morphedTree->AsCall()->IsTailCall());
noway_assert(stmt->GetNextStmt() == nullptr);
- GenTreeCall* call = morph->AsCall();
+ GenTreeCall* call = morphedTree->AsCall();
// Could either be
// - a tail call dispatched via helper in which case block will be ending with BBJ_THROW or
// - a fast call made as jmp in which case block will be ending with BBJ_RETURN and marked as containing
CLANG_FORMAT_COMMENT_ANCHOR;
#ifdef DEBUG
- tree->gtDebugFlags &= ~GTF_DEBUG_NODE_MORPHED;
+ oldTree->gtDebugFlags &= ~GTF_DEBUG_NODE_MORPHED;
#endif
noway_assert(compTailCallUsed);
- noway_assert((tree->gtOper == GT_CALL) && tree->AsCall()->IsTailCall());
+ noway_assert((oldTree->gtOper == GT_CALL) && oldTree->AsCall()->IsTailCall());
noway_assert(stmt->GetNextStmt() == nullptr);
- GenTreeCall* call = morph->AsCall();
+ GenTreeCall* call = morphedTree->AsCall();
// Could either be
// - a tail call dispatched via helper in which case block will be ending with BBJ_THROW or
if (verbose)
{
printf("\nfgMorphTree (stressClone from):\n");
- gtDispTree(morph);
+ gtDispTree(morphedTree);
}
- morph = gtCloneExpr(morph);
- noway_assert(morph);
+ morphedTree = gtCloneExpr(morphedTree);
+ noway_assert(morphedTree != nullptr);
if (verbose)
{
printf("\nfgMorphTree (stressClone to):\n");
- gtDispTree(morph);
+ gtDispTree(morphedTree);
}
}
/* If the hash value changes. we modified the tree during morphing */
if (verbose)
{
- unsigned newHash = gtHashValue(morph);
+ unsigned newHash = gtHashValue(morphedTree);
if (newHash != oldHash)
{
printf("\nfgMorphTree " FMT_BB ", " FMT_STMT " (after)\n", block->bbNum, stmt->GetID());
- gtDispTree(morph);
+ gtDispTree(morphedTree);
}
}
#endif
- /* Check for morph as a GT_COMMA with an unconditional throw */
- if (!gtIsActiveCSE_Candidate(morph) && fgIsCommaThrow(morph, true))
+ /* Check for morphedTree as a GT_COMMA with an unconditional throw */
+ if (!gtIsActiveCSE_Candidate(morphedTree) && fgIsCommaThrow(morphedTree, true))
{
/* Use the call as the new stmt */
- morph = morph->gtOp.gtOp1;
- noway_assert(morph->gtOper == GT_CALL);
- noway_assert((morph->gtFlags & GTF_COLON_COND) == 0);
+ morphedTree = morphedTree->gtOp.gtOp1;
+ noway_assert(morphedTree->gtOper == GT_CALL);
+ noway_assert((morphedTree->gtFlags & GTF_COLON_COND) == 0);
fgRemoveRestOfBlock = true;
}
- stmt->gtStmtExpr = tree = morph;
+ stmt->SetRootNode(morphedTree);
if (fgRemoveRestOfBlock)
{
Statement* first = block->firstStmt();
noway_assert(first);
Statement* lastStmt = block->lastStmt();
- noway_assert(lastStmt && lastStmt->gtNext == nullptr);
- GenTree* last = lastStmt->gtStmtExpr;
+ noway_assert(lastStmt && lastStmt->GetNextStmt() == nullptr);
+ GenTree* last = lastStmt->GetRootNode();
if (((block->bbJumpKind == BBJ_COND) && (last->gtOper == GT_JTRUE)) ||
((block->bbJumpKind == BBJ_SWITCH) && (last->gtOper == GT_SWITCH)))
op1->gtFlags &= ~GTF_RELOP_JMP_USED;
}
- lastStmt->gtStmtExpr = fgMorphTree(op1);
+ lastStmt->SetRootNode(fgMorphTree(op1));
}
}
// TODO: Need to characterize the last top level stmt of a block ending with BBJ_RETURN.
Statement* lastStmt = block->lastStmt();
- GenTree* ret = (lastStmt != nullptr) ? lastStmt->gtStmtExpr : nullptr;
+ GenTree* ret = (lastStmt != nullptr) ? lastStmt->GetRootNode() : nullptr;
if ((ret != nullptr) && (ret->OperGet() == GT_RETURN) && ((ret->gtFlags & GTF_RET_MERGED) != 0))
{
noway_assert(ret->gtGetOp1() != nullptr);
Statement* pAfterStatement = lastStmt;
- IL_OFFSETX offset = lastStmt->gtStmtILoffsx;
+ IL_OFFSETX offset = lastStmt->GetILOffsetX();
GenTree* tree =
gtNewTempAssign(genReturnLocal, ret->gtGetOp1(), &pAfterStatement, offset, block);
if (tree->OperIsCopyBlkOp())
if (pAfterStatement == lastStmt)
{
- lastStmt->gtStmtExpr = tree;
+ lastStmt->SetRootNode(tree);
}
else
{
}
// make sure that copy-prop ignores this assignment.
- lastStmt->gtStmtExpr->gtFlags |= GTF_DONT_CSE;
+ lastStmt->GetRootNode()->gtFlags |= GTF_DONT_CSE;
}
else if (ret != nullptr && ret->OperGet() == GT_RETURN)
{
}
#endif // DEBUG
- GenTree* expr = stmt->gtStmtExpr;
+ GenTree* expr = stmt->GetRootNode();
GenTree* dst = nullptr;
GenTree* qmark = fgGetTopLevelQmark(expr, &dst);
// Append cond1 as JTRUE to cond1Block
GenTree* jmpTree = gtNewOperNode(GT_JTRUE, TYP_VOID, condExpr);
- Statement* jmpStmt = fgNewStmtFromTree(jmpTree, stmt->gtStmtILoffsx);
+ Statement* jmpStmt = fgNewStmtFromTree(jmpTree, stmt->GetILOffsetX());
fgInsertStmtAtEnd(cond1Block, jmpStmt);
// Append cond2 as JTRUE to cond2Block
jmpTree = gtNewOperNode(GT_JTRUE, TYP_VOID, cond2Expr);
- jmpStmt = fgNewStmtFromTree(jmpTree, stmt->gtStmtILoffsx);
+ jmpStmt = fgNewStmtFromTree(jmpTree, stmt->GetILOffsetX());
fgInsertStmtAtEnd(cond2Block, jmpStmt);
// AsgBlock should get tmp = op1 assignment.
trueExpr = gtNewTempAssign(dst->AsLclVarCommon()->GetLclNum(), trueExpr);
- Statement* trueStmt = fgNewStmtFromTree(trueExpr, stmt->gtStmtILoffsx);
+ Statement* trueStmt = fgNewStmtFromTree(trueExpr, stmt->GetILOffsetX());
fgInsertStmtAtEnd(asgBlock, trueStmt);
// Since we are adding helper in the JTRUE false path, reverse the cond2 and add the helper.
gtReverseCond(cond2Expr);
GenTree* helperExpr = gtNewTempAssign(dst->AsLclVarCommon()->GetLclNum(), true2Expr);
- Statement* helperStmt = fgNewStmtFromTree(helperExpr, stmt->gtStmtILoffsx);
+ Statement* helperStmt = fgNewStmtFromTree(helperExpr, stmt->GetILOffsetX());
fgInsertStmtAtEnd(helperBlock, helperStmt);
// Finally remove the nested qmark stmt.
*/
void Compiler::fgExpandQmarkStmt(BasicBlock* block, Statement* stmt)
{
- GenTree* expr = stmt->gtStmtExpr;
+ GenTree* expr = stmt->GetRootNode();
// Retrieve the Qmark node to be expanded.
GenTree* dst = nullptr;
}
GenTree* jmpTree = gtNewOperNode(GT_JTRUE, TYP_VOID, qmark->gtGetOp1());
- Statement* jmpStmt = fgNewStmtFromTree(jmpTree, stmt->gtStmtILoffsx);
+ Statement* jmpStmt = fgNewStmtFromTree(jmpTree, stmt->GetILOffsetX());
fgInsertStmtAtEnd(condBlock, jmpStmt);
// Remove the original qmark statement.
{
trueExpr = gtNewTempAssign(lclNum, trueExpr);
}
- Statement* trueStmt = fgNewStmtFromTree(trueExpr, stmt->gtStmtILoffsx);
+ Statement* trueStmt = fgNewStmtFromTree(trueExpr, stmt->GetILOffsetX());
fgInsertStmtAtEnd(thenBlock, trueStmt);
}
{
falseExpr = gtNewTempAssign(lclNum, falseExpr);
}
- Statement* falseStmt = fgNewStmtFromTree(falseExpr, stmt->gtStmtILoffsx);
+ Statement* falseStmt = fgNewStmtFromTree(falseExpr, stmt->GetILOffsetX());
fgInsertStmtAtEnd(elseBlock, falseStmt);
}
{
for (Statement* stmt : block->Statements())
{
- GenTree* expr = stmt->gtStmtExpr;
+ GenTree* expr = stmt->GetRootNode();
#ifdef DEBUG
fgPreExpandQmarkChecks(expr);
#endif
{
for (Statement* stmt : block->Statements())
{
- GenTree* expr = stmt->gtStmtExpr;
+ GenTree* expr = stmt->GetRootNode();
fgWalkTreePre(&expr, Compiler::fgAssertNoQmark, nullptr);
}
}
}
#endif // DEBUG
- WalkTree(&stmt->gtStmtExpr, nullptr);
+ WalkTree(stmt->GetRootNodePointer(), nullptr);
// We could have something a statement like IND(ADDR(LCL_VAR)) so we need to escape
// the location here. This doesn't seem to happen often, if ever. The importer
if (m_stmtModified)
{
printf("LocalAddressVisitor modified statement:\n");
- m_compiler->gtDispTree(stmt->gtStmtExpr);
+ m_compiler->gtDispTree(stmt->GetRootNode());
m_compiler->gtDispStmt(stmt);
}
bool Compiler::fgMorphCombineSIMDFieldAssignments(BasicBlock* block, Statement* stmt)
{
- GenTree* tree = stmt->gtStmtExpr;
+ GenTree* tree = stmt->GetRootNode();
assert(tree->OperGet() == GT_ASG);
GenTree* originalLHS = tree->gtOp.gtOp1;
while (curStmt != nullptr && remainingAssignments > 0)
{
- GenTree* exp = curStmt->gtStmtExpr;
+ GenTree* exp = curStmt->GetRootNode();
if (exp->OperGet() != GT_ASG)
{
break;
GenTree* dstNode = gtNewOperNode(GT_IND, simdType, copyBlkDst);
tree = gtNewAssignNode(dstNode, simdStructNode);
- stmt->gtStmtExpr = tree;
+ stmt->SetRootNode(tree);
// Since we generated a new address node which didn't exist before,
// we should expose this address manually here.
//
// See impIsTailCallILPattern() for details on tail call IL patterns
// that are supported.
- GenTree* callExpr = callStmt->gtStmtExpr;
+ GenTree* callExpr = callStmt->GetRootNode();
if (callExpr->gtOper != GT_RETURN)
{
// Check to see if there is a pop.
// Since tail call is honored, we can get rid of the stmt corresponding to pop.
- if (nextMorphStmt != nullptr && nextMorphStmt->gtStmtExpr->gtOper != GT_RETURN)
+ if (nextMorphStmt != nullptr && nextMorphStmt->GetRootNode()->gtOper != GT_RETURN)
{
// Note that pop opcode may or may not result in a new stmt (for details see
// impImportBlockCode()). Hence, it is not possible to assert about the IR
// Side effect flags on a GT_COMMA may be overly pessimistic, so examine
// the constituent nodes.
- GenTree* popExpr = popStmt->gtStmtExpr;
+ GenTree* popExpr = popStmt->GetRootNode();
bool isSideEffectFree = (popExpr->gtFlags & GTF_ALL_EFFECT) == 0;
if (!isSideEffectFree && (popExpr->OperGet() == GT_COMMA))
{
// 3) lclVar = callResultLclVar, the actual ret(lclVar) in another block
if (nextMorphStmt != nullptr)
{
- GenTree* callExpr = callStmt->gtStmtExpr;
+ GenTree* callExpr = callStmt->GetRootNode();
if (callExpr->gtOper != GT_ASG)
{
// The next stmt can be GT_RETURN(TYP_VOID) or GT_RETURN(lclVar),
// where lclVar was return buffer in the call for structs or simd.
Statement* retStmt = nextMorphStmt;
- GenTree* retExpr = retStmt->gtStmtExpr;
+ GenTree* retExpr = retStmt->GetRootNode();
noway_assert(retExpr->gtOper == GT_RETURN);
nextMorphStmt = retStmt->GetNextStmt();
// We can have a move from the call result to an lvaInlineeReturnSpillTemp.
// However, we can't check that this assignment was created there.
- if (nextMorphStmt->gtStmtExpr->gtOper == GT_ASG)
+ if (nextMorphStmt->GetRootNode()->gtOper == GT_ASG)
{
Statement* moveStmt = nextMorphStmt;
- GenTree* moveExpr = nextMorphStmt->gtStmtExpr;
+ GenTree* moveExpr = nextMorphStmt->GetRootNode();
noway_assert(moveExpr->gtGetOp1()->OperIsLocal() && moveExpr->gtGetOp2()->OperIsLocal());
unsigned srcLclNum = moveExpr->gtGetOp2()->AsLclVarCommon()->GetLclNum();
#endif
{
Statement* retStmt = nextMorphStmt;
- GenTree* retExpr = nextMorphStmt->gtStmtExpr;
+ GenTree* retExpr = nextMorphStmt->GetRootNode();
noway_assert(retExpr->gtOper == GT_RETURN);
GenTree* treeWithLcl = retExpr->gtGetOp1();
for (Statement* stmt : block->Statements())
{
BuildConnGraphVisitor buildConnGraphVisitor(this);
- buildConnGraphVisitor.WalkTree(&stmt->gtStmtExpr, nullptr);
+ buildConnGraphVisitor.WalkTree(stmt->GetRootNodePointer(), nullptr);
}
}
}
for (Statement* stmt : block->Statements())
{
- GenTree* stmtExpr = stmt->gtStmtExpr;
+ GenTree* stmtExpr = stmt->GetRootNode();
GenTree* op2 = nullptr;
bool canonicalAllocObjFound = false;
assert(basicBlockHasNewObj);
//------------------------------------------------------------------------
// We expect the following expression tree at this point
- // * STMT void
- // | /--* ALLOCOBJ ref
- // | | \--* CNS_INT(h) long
- // \--* ASG ref
- // \--* LCL_VAR ref
+ // STMTx (IL 0x... ???)
+ // * ASG ref
+ // +--* LCL_VAR ref
+ // \--* ALLOCOBJ ref
+ // \--* CNS_INT(h) long
//------------------------------------------------------------------------
GenTree* op1 = stmtExpr->gtGetOp1();
// definitely-stack-pointing pointers. All definitely-stack-pointing pointers are in both sets.
MarkLclVarAsDefinitelyStackPointing(lclNum);
MarkLclVarAsPossiblyStackPointing(lclNum);
- stmt->gtStmtExpr->gtBashToNOP();
+ stmt->GetRootNode()->gtBashToNOP();
comp->optMethodFlags |= OMF_HAS_OBJSTACKALLOC;
didStackAllocate = true;
}
{
// We assume that GT_ALLOCOBJ nodes are always present in the
// canonical form.
- comp->fgWalkTreePre(&stmt->gtStmtExpr, AssertWhenAllocObjFoundVisitor);
+ comp->fgWalkTreePre(stmt->GetRootNodePointer(), AssertWhenAllocObjFoundVisitor);
}
#endif // DEBUG
}
unsigned int structSize = comp->lvaTable[lclNum].lvSize();
//------------------------------------------------------------------------
- // * STMT void
- // | /--* CNS_INT int 0
- // \--* ASG struct (init)
- // \--* LCL_VAR struct
+ // STMTx (IL 0x... ???)
+ // * ASG struct (init)
+ // +--* LCL_VAR struct
+ // \--* CNS_INT int 0
//------------------------------------------------------------------------
GenTree* tree = comp->gtNewLclvNode(lclNum, TYP_STRUCT);
}
//------------------------------------------------------------------------
- // * STMT void
- // | /--* CNS_INT(h) long
- // \--* ASG long
- // \--* FIELD long #PseudoField:0x0
- // \--* ADDR byref
- // \--* LCL_VAR struct
+ // STMTx (IL 0x... ???)
+ // * ASG long
+ // +--* FIELD long #PseudoField:0x0
+ // | \--* ADDR byref
+ // | \--* LCL_VAR struct
+ // \--* CNS_INT(h) long
//------------------------------------------------------------------------
// Create a local representing the object
for (Statement* stmt : block->Statements())
{
RewriteUsesVisitor rewriteUsesVisitor(this);
- rewriteUsesVisitor.WalkTree(&stmt->gtStmtExpr, nullptr);
+ rewriteUsesVisitor.WalkTree(stmt->GetRootNodePointer(), nullptr);
}
}
}
{
/* We walk the tree in the forwards direction (bottom up) */
bool stmtHasArrLenCandidate = false;
- for (GenTree* tree = stmt->gtStmtList; tree != nullptr; tree = tree->gtNext)
+ for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext)
{
if (tree->OperIsCompare() && stmtHasArrLenCandidate)
{
{
// We walk the tree in the forwards direction (bottom up)
- for (GenTree* tree = stmt->gtStmtList; tree != nullptr; tree = tree->gtNext)
+ for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext)
{
if (IS_CSE_INDEX(tree->gtCSEnum))
{
for (Statement* stmt : StatementList(block->FirstNonPhiDef()))
{
// We must clear the gtCSEnum field.
- for (GenTree* tree = stmt->gtStmtExpr; tree; tree = tree->gtPrev)
+ for (GenTree* tree = stmt->GetRootNode(); tree; tree = tree->gtPrev)
{
tree->gtCSEnum = NO_CSE;
}
// Walk the statement trees in this basic block
for (Statement* stmt : StatementList(block->FirstNonPhiDef()))
{
- for (GenTree* tree = stmt->gtStmtExpr; tree; tree = tree->gtPrev)
+ for (GenTree* tree = stmt->GetRootNode(); tree; tree = tree->gtPrev)
{
assert(tree->gtCSEnum == NO_CSE);
}
//
bool Compiler::optIsLoopTestEvalIntoTemp(Statement* testStmt, Statement** newTestStmt)
{
- GenTree* test = testStmt->gtStmtExpr;
+ GenTree* test = testStmt->GetRootNode();
if (test->gtOper != GT_JTRUE)
{
{
// Get the previous statement to get the def (rhs) of Vtmp to see
// if the "test" is evaluated into Vtmp.
- Statement* prevStmt = testStmt->getPrevStmt();
+ Statement* prevStmt = testStmt->GetPrevStmt();
if (prevStmt == nullptr)
{
return false;
}
- GenTree* tree = prevStmt->gtStmtExpr;
+ GenTree* tree = prevStmt->GetRootNode();
if (tree->OperGet() == GT_ASG)
{
GenTree* lhs = tree->gtOp.gtOp1;
// and the loop termination test.
noway_assert(bottom->bbStmtList != nullptr);
Statement* testStmt = bottom->lastStmt();
- noway_assert(testStmt != nullptr && testStmt->gtNext == nullptr);
+ noway_assert(testStmt != nullptr && testStmt->GetNextStmt() == nullptr);
Statement* newTestStmt;
if (optIsLoopTestEvalIntoTemp(testStmt, &newTestStmt))
// Check if we have the incr stmt before the test stmt, if we don't,
// check if incr is part of the loop "top".
- Statement* incrStmt = testStmt->getPrevStmt();
- if (incrStmt == nullptr || optIsLoopIncrTree(incrStmt->gtStmtExpr) == BAD_VAR_NUM)
+ Statement* incrStmt = testStmt->GetPrevStmt();
+ if (incrStmt == nullptr || optIsLoopIncrTree(incrStmt->GetRootNode()) == BAD_VAR_NUM)
{
- if (top == nullptr || top->bbStmtList == nullptr || top->bbStmtList->gtPrev == nullptr)
+ if (top == nullptr || top->bbStmtList == nullptr || top->bbStmtList->GetPrevStmt() == nullptr)
{
return false;
}
// If the prev stmt to loop test is not incr, then check if we have loop test evaluated into a tmp.
Statement* toplastStmt = top->lastStmt();
- if (optIsLoopIncrTree(toplastStmt->gtStmtExpr) != BAD_VAR_NUM)
+ if (optIsLoopIncrTree(toplastStmt->GetRootNode()) != BAD_VAR_NUM)
{
incrStmt = toplastStmt;
}
return false;
}
- Statement* initStmt = phdrStmt->getPrevStmt();
- noway_assert(initStmt != nullptr && (initStmt->gtNext == nullptr));
+ Statement* initStmt = phdrStmt->GetPrevStmt();
+ noway_assert(initStmt != nullptr && (initStmt->GetNextStmt() == nullptr));
// If it is a duplicated loop condition, skip it.
- if (initStmt->compilerAdded)
+ if (initStmt->IsCompilerAdded())
{
bool doGetPrev = true;
#ifdef DEBUG
{
// Previous optimization passes may have inserted compiler-generated
// statements other than duplicated loop conditions.
- doGetPrev = (initStmt->gtPrev != nullptr);
+ doGetPrev = (initStmt->GetPrevStmt() != nullptr);
}
else
{
// Must be a duplicated loop condition.
- noway_assert(initStmt->gtStmtExpr->gtOper == GT_JTRUE);
+ noway_assert(initStmt->GetRootNode()->gtOper == GT_JTRUE);
}
#endif // DEBUG
if (doGetPrev)
{
- initStmt = initStmt->getPrevStmt();
+ initStmt = initStmt->GetPrevStmt();
}
noway_assert(initStmt != nullptr);
}
- *ppInit = initStmt->gtStmtExpr;
- *ppTest = testStmt->gtStmtExpr;
- *ppIncr = incrStmt->gtStmtExpr;
+ *ppInit = initStmt->GetRootNode();
+ *ppTest = testStmt->GetRootNode();
+ *ppIncr = incrStmt->GetRootNode();
return true;
}
block = block->bbNext;
for (Statement* stmt : block->Statements())
{
- if (stmt->gtStmtExpr == incr)
+ if (stmt->GetRootNode() == incr)
{
break;
}
printf("\n");
- gtDispTree(stmt->gtStmtExpr);
+ gtDispTree(stmt->GetRootNode());
}
} while (block != bottom);
}
// Locate/initialize the increment/test statements.
Statement* initStmt = head->lastStmt();
- noway_assert((initStmt != nullptr) && (initStmt->gtNext == nullptr));
+ noway_assert((initStmt != nullptr) && (initStmt->GetNextStmt() == nullptr));
Statement* testStmt = bottom->lastStmt();
- noway_assert((testStmt != nullptr) && (testStmt->gtNext == nullptr));
- Statement* incrStmt = testStmt->gtPrevStmt;
+ noway_assert((testStmt != nullptr) && (testStmt->GetNextStmt() == nullptr));
+ Statement* incrStmt = testStmt->GetPrevStmt();
noway_assert(incrStmt != nullptr);
- if (initStmt->compilerAdded)
+ if (initStmt->IsCompilerAdded())
{
/* Must be a duplicated loop condition */
- noway_assert(initStmt->gtStmtExpr->gtOper == GT_JTRUE);
+ noway_assert(initStmt->GetRootNode()->gtOper == GT_JTRUE);
dupCond = true;
- initStmt = initStmt->gtPrevStmt;
+ initStmt = initStmt->GetPrevStmt();
noway_assert(initStmt != nullptr);
}
else
continue;
}
- GenTree* incr = incrStmt->gtStmtExpr;
+ GenTree* incr = incrStmt->GetRootNode();
// Don't unroll loops we don't understand.
if (incr->gtOper != GT_ASG)
}
incr = incr->gtOp.gtOp2;
- GenTree* init = initStmt->gtStmtExpr;
- GenTree* test = testStmt->gtStmtExpr;
+ GenTree* init = initStmt->GetRootNode();
+ GenTree* test = testStmt->GetRootNode();
/* Make sure everything looks ok */
if ((init->gtOper != GT_ASG) || (init->gtOp.gtOp1->gtOper != GT_LCL_VAR) ||
(incr->gtOp.gtOp1->gtLclVarCommon.GetLclNum() != lvar) || (incr->gtOp.gtOp2->gtOper != GT_CNS_INT) ||
(incr->gtOp.gtOp2->gtIntCon.gtIconVal != iterInc) ||
- (testStmt->gtStmtExpr->gtOper != GT_JTRUE))
+ (testStmt->GetRootNode()->gtOper != GT_JTRUE))
{
noway_assert(!"Bad precondition in Compiler::optUnrollLoops()");
continue;
// Remove the test; we're doing a full unroll.
Statement* testCopyStmt = newBlock->lastStmt();
- GenTree* testCopyExpr = testCopyStmt->gtStmtExpr;
+ GenTree* testCopyExpr = testCopyStmt->GetRootNode();
assert(testCopyExpr->gtOper == GT_JTRUE);
GenTree* sideEffList = nullptr;
gtExtractSideEffList(testCopyExpr, &sideEffList, GTF_SIDE_EFFECT | GTF_ORDER_SIDEEFF);
}
else
{
- testCopyStmt->gtStmtExpr = sideEffList;
+ testCopyStmt->SetRootNode(sideEffList);
}
newBlock->bbJumpKind = BBJ_NONE;
{
Statement* preHeaderStmt = head->firstStmt();
noway_assert(preHeaderStmt != nullptr);
- testStmt = preHeaderStmt->gtPrevStmt;
+ testStmt = preHeaderStmt->GetPrevStmt();
- noway_assert((testStmt != nullptr) && (testStmt->gtNext == nullptr));
- noway_assert(testStmt->gtStmtExpr->gtOper == GT_JTRUE);
+ noway_assert((testStmt != nullptr) && (testStmt->GetNextStmt() == nullptr));
+ noway_assert(testStmt->GetRootNode()->gtOper == GT_JTRUE);
- initStmt = testStmt->gtPrevStmt;
- noway_assert((initStmt != nullptr) && (initStmt->gtNext == testStmt));
+ initStmt = testStmt->GetPrevStmt();
+ noway_assert((initStmt != nullptr) && (initStmt->GetNextStmt() == testStmt));
- initStmt->gtNext = nullptr;
- preHeaderStmt->gtPrev = initStmt;
- head->bbJumpKind = BBJ_NONE;
+ initStmt->SetNextStmt(nullptr);
+ preHeaderStmt->SetPrevStmt(initStmt);
+ head->bbJumpKind = BBJ_NONE;
head->bbFlags &= ~BBF_NEEDS_GCPOLL;
}
else
{
printf("Whole unrolled loop:\n");
- gtDispTree(initStmt->gtStmtExpr);
+ gtDispTree(initStmt->GetRootNode());
printf("\n");
fgDumpTrees(head->bbNext, insertAfter);
}
assert(testStmt != nullptr);
- Statement* result = testStmt->getPrevStmt();
+ Statement* result = testStmt->GetPrevStmt();
#ifdef DEBUG
- while (testStmt->gtNext != nullptr)
+ while (testStmt->GetNextStmt() != nullptr)
{
testStmt = testStmt->GetNextStmt();
}
/* Get to the condition node from the statement tree */
- GenTree* condTree = condStmt->gtStmtExpr;
+ GenTree* condTree = condStmt->GetRootNode();
noway_assert(condTree->gtOper == GT_JTRUE);
condTree = condTree->gtOp.gtOp1;
Statement* copyOfCondStmt = fgNewStmtAtEnd(block, condTree);
- copyOfCondStmt->compilerAdded = true;
+ copyOfCondStmt->SetCompilerAdded();
if (opts.compDbgInfo)
{
- copyOfCondStmt->gtStmtILoffsx = condStmt->gtStmtILoffsx;
+ copyOfCondStmt->SetILOffsetX(condStmt->GetILOffsetX());
}
// Flag the block that received the copy as potentially having an array/vtable
for (Statement* stmt : beg->Statements())
{
- if (fgWalkTreePre(&stmt->gtStmtExpr, optIsVarAssgCB, &desc))
+ if (fgWalkTreePre(stmt->GetRootNodePointer(), optIsVarAssgCB, &desc))
{
result = true;
goto DONE;
for (Statement* stmt : StatementList(beg->FirstNonPhiDef()))
{
- fgWalkTreePre(&stmt->gtStmtExpr, optIsVarAssgCB, &desc);
+ fgWalkTreePre(stmt->GetRootNodePointer(), optIsVarAssgCB, &desc);
if (desc.ivaMaskIncomplete)
{
compCurBB = preHead;
hoist = fgMorphTree(hoist);
- Statement* hoistStmt = gtNewStmt(hoist);
- hoistStmt->compilerAdded = true;
+ Statement* hoistStmt = gtNewStmt(hoist);
+ hoistStmt->SetCompilerAdded();
/* simply append the statement at the end of the preHead's list */
/* append after last statement */
Statement* lastStmt = preHead->lastStmt();
- assert(lastStmt->gtNext == nullptr);
+ assert(lastStmt->GetNextStmt() == nullptr);
- lastStmt->gtNext = hoistStmt;
- hoistStmt->gtPrev = lastStmt;
- firstStmt->gtPrev = hoistStmt;
+ lastStmt->SetNextStmt(hoistStmt);
+ hoistStmt->SetPrevStmt(lastStmt);
+ firstStmt->SetPrevStmt(hoistStmt);
}
else
{
/* Empty pre-header - store the single statement in the block */
preHead->bbStmtList = hoistStmt;
- hoistStmt->gtPrev = hoistStmt;
+ hoistStmt->SetPrevStmt(hoistStmt);
}
- hoistStmt->gtNext = nullptr;
+ hoistStmt->SetNextStmt(nullptr);
#ifdef DEBUG
if (verbose)
{
for (Statement* stmt : StatementList(block->FirstNonPhiDef()))
{
- WalkTree(&stmt->gtStmtExpr, nullptr);
- assert(m_valueStack.TopRef().Node() == stmt->gtStmtExpr);
+ WalkTree(stmt->GetRootNodePointer(), nullptr);
+ assert(m_valueStack.TopRef().Node() == stmt->GetRootNode());
if (m_valueStack.TopRef().m_hoistable)
{
- m_compiler->optHoistCandidate(stmt->gtStmtExpr, m_loopNum, m_hoistContext);
+ m_compiler->optHoistCandidate(stmt->GetRootNode(), m_loopNum, m_hoistContext);
}
m_valueStack.Reset();
for (Statement* stmt : top->Statements())
{
- GenTree* tree = stmt->gtStmtExpr;
+ GenTree* tree = stmt->GetRootNode();
if (tree->OperGet() != GT_ASG)
{
break;
// Now iterate over the remaining statements, and their trees.
for (Statement* stmt : StatementList(blk->FirstNonPhiDef()))
{
- for (GenTree* tree = stmt->gtStmtList; tree != nullptr; tree = tree->gtNext)
+ for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext)
{
genTreeOps oper = tree->OperGet();
info.stmt = stmt;
const bool lclVarsOnly = false;
const bool computeStack = false;
- fgWalkTreePre(&stmt->gtStmtExpr, optCanOptimizeByLoopCloningVisitor, &info, lclVarsOnly, computeStack);
+ fgWalkTreePre(stmt->GetRootNodePointer(), optCanOptimizeByLoopCloningVisitor, &info, lclVarsOnly,
+ computeStack);
}
}
//
// TODO-CQ: CLONE: After morph make sure this method extracts values before morph.
//
-// [000024] ------------ * STMT void(IL 0x007...0x00C)
-// [000021] a--XG+------ | /--* IND int
-// [000045] -----+------ | | | /--* CNS_INT long 16 Fseq[#FirstElem]
-// [000046] -----+------ | | | /--* ADD long
-// [000043] -----+-N---- | | | | | /--* CNS_INT long 2
-// [000044] -----+------ | | | | \--* LSH long
-// [000042] -----+------ | | | | \--* CAST long < -int
-// [000039] i----+------ | | | | \--* LCL_VAR int V04 loc0
-// [000047] -----+------ | | \--* ADD byref
-// [000038] -----+------ | | \--* LCL_VAR ref V00 arg0
-// [000048] ---XG+------ | /--* COMMA int
-// [000041] ---X-+------ | | \--* ARR_BOUNDS_CHECK_Rng void
-// [000020] -----+------ | | +--* LCL_VAR int V04 loc0
-// [000040] ---X-+------ | | \--* ARR_LENGTH int
-// [000019] -----+------ | | \--* LCL_VAR ref V00 arg0
-// [000023] -A-XG+------ \--* ASG int
-// [000022] D----+-N---- \--* LCL_VAR int V06 tmp1
+// STMT void(IL 0x007...0x00C)
+// [000023] -A-XG+------ * ASG int
+// [000022] D----+-N---- +--* LCL_VAR int V06 tmp1
+// [000048] ---XG+------ \--* COMMA int
+// [000041] ---X-+------ +--* ARR_BOUNDS_CHECK_Rng void
+// [000020] -----+------ | +--* LCL_VAR int V04 loc0
+// [000040] ---X-+------ | \--* ARR_LENGTH int
+// [000019] -----+------ | \--* LCL_VAR ref V00 arg0
+// [000021] a--XG+------ \--* IND int
+// [000047] -----+------ \--* ADD byref
+// [000038] -----+------ +--* LCL_VAR ref V00 arg0
+// [000046] -----+------ \--* ADD long
+// [000044] -----+------ +--* LSH long
+// [000042] -----+------ | +--* CAST long < -int
+// [000039] i----+------ | | \--* LCL_VAR int V04 loc0
+// [000043] -----+-N---- | \--* CNS_INT long 2
+// [000045] -----+------ \--* CNS_INT long 16 Fseq[#FirstElem]
bool Compiler::optExtractArrIndex(GenTree* tree, ArrIndex* result, unsigned lhsNum)
{
}
noway_assert(condBlock->bbJumpKind == BBJ_COND);
- GenTree* cond = condBlock->lastStmt()->gtStmtExpr;
+ GenTree* cond = condBlock->lastStmt()->GetRootNode();
noway_assert(cond->gtOper == GT_JTRUE);
/* The second block must contain a single statement */
Statement* s2 = b2->firstStmt();
- if (s2->gtPrev != s2)
+ if (s2->GetPrevStmt() != s2)
{
continue;
}
- GenTree* t2 = s2->gtStmtExpr;
+ GenTree* t2 = s2->GetRootNode();
noway_assert(t2->gtOper == GT_JTRUE);
/* Find the condition for the first block */
Statement* s1 = b1->lastStmt();
- GenTree* t1 = s1->gtStmtExpr;
+ GenTree* t1 = s1->GetRootNode();
noway_assert(t1->gtOper == GT_JTRUE);
if (b2->countOfInEdges() > 1)
for (Statement* stmt : block->Statements())
{
MapMethodDefsData data(this, block, stmt);
- m_pCompiler->fgWalkTreePre(&stmt->gtStmtExpr, MapMethodDefsVisitor, &data, false, true);
+ m_pCompiler->fgWalkTreePre(stmt->GetRootNodePointer(), MapMethodDefsVisitor, &data, false, true);
}
}
m_fMappedDefs = true;
{
for (Statement* stmt : block->Statements())
{
- for (GenTree* tree = stmt->gtStmtList; tree; tree = tree->gtNext)
+ for (GenTree* tree = stmt->GetTreeList(); tree; tree = tree->gtNext)
{
if (IsOverBudget())
{
{
ValidateStatement(statement, block);
- for (GenTree* tree = statement->gtStmtList; tree; tree = tree->gtNext)
+ for (GenTree* tree = statement->GetTreeList(); tree; tree = tree->gtNext)
{
// QMARK nodes should have been removed before this phase.
assert(tree->OperGet() != GT_QMARK);
for (Statement* statement : StatementList(firstStatement))
{
- assert(statement->gtStmtList != nullptr);
- assert(statement->gtStmtList->gtPrev == nullptr);
- assert(statement->gtStmtExpr != nullptr);
- assert(statement->gtStmtExpr->gtNext == nullptr);
+ assert(statement->GetTreeList() != nullptr);
+ assert(statement->GetTreeList()->gtPrev == nullptr);
+ assert(statement->GetRootNode() != nullptr);
+ assert(statement->GetRootNode()->gtNext == nullptr);
- BlockRange().InsertAtEnd(LIR::Range(statement->gtStmtList, statement->gtStmtExpr));
+ BlockRange().InsertAtEnd(LIR::Range(statement->GetTreeList(), statement->GetRootNode()));
// If this statement has correct offset information, change it into an IL offset
// node and insert it into the LIR.
- if (statement->gtStmtILoffsx != BAD_IL_OFFSET)
+ if (statement->GetILOffsetX() != BAD_IL_OFFSET)
{
assert(!statement->IsPhiDefnStmt());
GenTreeILOffset* ilOffset = new (comp, GT_IL_OFFSET)
- GenTreeILOffset(statement->gtStmtILoffsx DEBUGARG(statement->gtStmtLastILoffs));
- BlockRange().InsertBefore(statement->gtStmtList, ilOffset);
+ GenTreeILOffset(statement->GetILOffsetX() DEBUGARG(statement->GetLastILOffset()));
+ BlockRange().InsertBefore(statement->GetTreeList(), ilOffset);
}
m_block = block;
- visitor.WalkTree(&statement->gtStmtExpr, nullptr);
+ visitor.WalkTree(statement->GetRootNodePointer(), nullptr);
}
block->bbStmtList = nullptr;
{
return;
}
- GenTree* expr = stmt->gtStmtExpr;
+ GenTree* expr = stmt->GetRootNode();
if (expr->OperGet() == GT_ASG && expr->TypeGet() == TYP_FLOAT)
{
GenTree* curDst = expr->gtOp.gtOp1;
else if (fgPreviousCandidateSIMDFieldAsgStmt != nullptr)
{
assert(index > 0);
- GenTree* prevAsgExpr = fgPreviousCandidateSIMDFieldAsgStmt->gtStmtExpr;
+ GenTree* prevAsgExpr = fgPreviousCandidateSIMDFieldAsgStmt->GetRootNode();
GenTree* prevDst = prevAsgExpr->gtOp.gtOp1;
GenTree* prevSrc = prevAsgExpr->gtOp.gtOp2;
if (!areArgumentsContiguous(prevDst, curDst) || !areArgumentsContiguous(prevSrc, curSrc))
blk->bbStmtList = blk->FirstNonPhiDef();
if (blk->bbStmtList != nullptr)
{
- blk->bbStmtList->gtPrev = last;
+ blk->bbStmtList->SetPrevStmt(last);
}
}
blk->bbPostOrderNum = 0;
for (Statement* stmt : blk->Statements())
{
- for (GenTree* tree = stmt->gtStmtList; tree != nullptr; tree = tree->gtNext)
+ for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext)
{
if (tree->IsLocal())
{
break;
}
- GenTree* tree = stmt->gtStmtExpr;
+ GenTree* tree = stmt->GetRootNode();
GenTree* phiLhs = tree->gtOp.gtOp1;
assert(phiLhs->OperGet() == GT_LCL_VAR);
asg->SetCosts(0, 0);
// Create the statement and chain everything in linear order - PHI, LCL_VAR, ASG
- Statement* stmt = m_pCompiler->gtNewStmt(asg);
- stmt->gtStmtList = phi;
- phi->gtNext = lhs;
- lhs->gtPrev = phi;
- lhs->gtNext = asg;
- asg->gtPrev = lhs;
+ Statement* stmt = m_pCompiler->gtNewStmt(asg);
+ stmt->SetTreeList(phi);
+ phi->gtNext = lhs;
+ lhs->gtPrev = phi;
+ lhs->gtNext = asg;
+ asg->gtPrev = lhs;
#ifdef DEBUG
unsigned seqNum = 1;
- for (GenTree* node = stmt->gtStmtList; node != nullptr; node = node->gtNext)
+ for (GenTree* node = stmt->GetTreeList(); node != nullptr; node = node->gtNext)
{
node->gtSeqNum = seqNum++;
}
// will be first in linear order as well.
phi->gtUses = new (m_pCompiler, CMK_ASTNode) GenTreePhi::Use(phiArg, phi->gtUses);
- GenTree* head = stmt->gtStmtList;
+ GenTree* head = stmt->GetTreeList();
assert(head->OperIs(GT_PHI, GT_PHI_ARG));
- stmt->gtStmtList = phiArg;
- phiArg->gtNext = head;
- head->gtPrev = phiArg;
+ stmt->SetTreeList(phiArg);
+ phiArg->gtNext = head;
+ head->gtPrev = phiArg;
#ifdef DEBUG
unsigned seqNum = 1;
- for (GenTree* node = stmt->gtStmtList; node != nullptr; node = node->gtNext)
+ for (GenTree* node = stmt->GetTreeList(); node != nullptr; node = node->gtNext)
{
node->gtSeqNum = seqNum++;
}
break;
}
- GenTree* tree = stmt->gtStmtExpr;
+ GenTree* tree = stmt->GetRootNode();
assert(tree->IsPhiDefn());
isPhiDefn = false;
}
- for (GenTree* tree = stmt->gtStmtList; tree; tree = tree->gtNext)
+ for (GenTree* tree = stmt->GetTreeList(); tree; tree = tree->gtNext)
{
TreeRenameVariables(tree, block, pRenameState, isPhiDefn);
}
break;
}
- GenTree* tree = stmt->gtStmtExpr;
+ GenTree* tree = stmt->GetRootNode();
GenTreePhi* phi = tree->gtGetOp2()->AsPhi();
unsigned lclNum = tree->gtOp.gtOp1->gtLclVar.GetLclNum();
for (Statement* stmt : handlerStart->Statements())
{
- GenTree* tree = stmt->gtStmtExpr;
+ GenTree* tree = stmt->GetRootNode();
// Check if the first n of the statements are phi nodes. If not, exit.
if (tree->OperGet() != GT_ASG || tree->gtOp.gtOp2 == nullptr ||
// Now iterate over the block's statements, and their trees.
for (Statement* stmt : StatementList(blk->FirstNonPhiDef()))
{
- for (GenTree* tree = stmt->gtStmtList; tree != nullptr; tree = tree->gtNext)
+ for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext)
{
tree->gtVNPair.SetBoth(ValueNumStore::NoVN);
}
// first check to see if all phi args have the same value.
for (; (stmt != nullptr) && stmt->IsPhiDefnStmt(); stmt = stmt->GetNextStmt())
{
- GenTree* asg = stmt->gtStmtExpr;
+ GenTree* asg = stmt->GetRootNode();
assert(asg->OperIs(GT_ASG));
GenTreeLclVar* newSsaDef = asg->AsOp()->gtGetOp1()->AsLclVar();
if (verbose)
{
printf("\n***** " FMT_BB ", " FMT_STMT "(before)\n", blk->bbNum, stmt->GetID());
- gtDispTree(stmt->gtStmtExpr);
+ gtDispTree(stmt->GetRootNode());
printf("\n");
}
#endif
- for (GenTree* tree = stmt->gtStmtList; tree != nullptr; tree = tree->gtNext)
+ for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext)
{
fgValueNumberTree(tree);
}
if (verbose)
{
printf("\n***** " FMT_BB ", " FMT_STMT "(after)\n", blk->bbNum, stmt->GetID());
- gtDispTree(stmt->gtStmtExpr);
+ gtDispTree(stmt->GetRootNode());
printf("\n");
- if (stmt->gtNext)
+ if (stmt->GetNextStmt() != nullptr)
{
printf("---------\n");
}