Fix reading Time zone rules using Julian days (#17672)
[platform/upstream/coreclr.git] / src / jit / gschecks.cpp
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5 /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
6 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
7 XX                                                                           XX
8 XX                               GSChecks                                    XX
9 XX                                                                           XX
10 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
11 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
12 */
13
14 #include "jitpch.h"
15 #ifdef _MSC_VER
16 #pragma hdrstop
17 #endif
18
19 /*****************************************************************************
20  * gsGSChecksInitCookie
21  * Grabs the cookie for detecting overflow of unsafe buffers.
22  */
23 void Compiler::gsGSChecksInitCookie()
24 {
25     var_types type = TYP_I_IMPL;
26
27     lvaGSSecurityCookie = lvaGrabTemp(false DEBUGARG("GSSecurityCookie"));
28
29     // Prevent cookie init/check from being optimized
30     lvaSetVarAddrExposed(lvaGSSecurityCookie);
31     lvaTable[lvaGSSecurityCookie].lvType = type;
32
33     info.compCompHnd->getGSCookie(&gsGlobalSecurityCookieVal, &gsGlobalSecurityCookieAddr);
34 }
35
36 const unsigned NO_SHADOW_COPY = UINT_MAX;
37
38 /*****************************************************************************
39  * gsCopyShadowParams
40  * The current function has an unsafe buffer on the stack.  Search for vulnerable
41  * parameters which could be used to modify a code address and take over the process
42  * in the case of a buffer overrun. Create a safe local copy for each vulnerable parameter,
43  * which will be allocated bellow the unsafe buffer.  Change uses of the param to the
44  * shadow copy.
45  *
46  * A pointer under indirection is considered vulnerable. A malicious user could read from
47  * protected memory or write to it. If a parameter is assigned/computed into another variable,
48  * and is a pointer (i.e., under indirection), then we consider the variable to be part of the
49  * equivalence class with the parameter. All parameters in the equivalence class are shadowed.
50  */
51 void Compiler::gsCopyShadowParams()
52 {
53     if (info.compIsVarArgs)
54     {
55         return;
56     }
57
58     // Allocate array for shadow param info
59     gsShadowVarInfo = new (this, CMK_Unknown) ShadowParamVarInfo[lvaCount]();
60
61     // Find groups of variables assigned to each other, and also
62     // tracks variables which are dereferenced and marks them as ptrs.
63     // Look for assignments to *p, and ptrs passed to functions
64     if (gsFindVulnerableParams())
65     {
66         // Replace vulnerable params by shadow copies.
67         gsParamsToShadows();
68     }
69 }
70
71 // This struct tracks how a tree is being used
72
73 struct MarkPtrsInfo
74 {
75     Compiler* comp;
76     unsigned  lvAssignDef;  // Which local variable is the tree being assigned to?
77     bool      isAssignSrc;  // Is this the source value for an assignment?
78     bool      isUnderIndir; // Is this a pointer value tree that is being dereferenced?
79     bool      skipNextNode; // Skip a single node during the tree-walk
80
81 #ifdef DEBUG
82     void Print()
83     {
84         printf(
85             "[MarkPtrsInfo] = {comp = %p, lvAssignDef = %d, isAssignSrc = %d, isUnderIndir = %d, skipNextNode = %d}\n",
86             comp, lvAssignDef, isAssignSrc, isUnderIndir, skipNextNode);
87     }
88 #endif
89 };
90
91 /*****************************************************************************
92  * gsMarkPtrsAndAssignGroups
93  * Walk a tree looking for assignment groups, variables whose value is used
94  * in a *p store or use, and variable passed to calls.  This info is then used
95  * to determine parameters which are vulnerable.
96  * This function carries a state to know if it is under an assign node, call node
97  * or indirection node.  It starts a new tree walk for it's subtrees when the state
98  * changes.
99  */
100 Compiler::fgWalkResult Compiler::gsMarkPtrsAndAssignGroups(GenTree** pTree, fgWalkData* data)
101 {
102     struct MarkPtrsInfo* pState        = (MarkPtrsInfo*)data->pCallbackData;
103     struct MarkPtrsInfo  newState      = *pState;
104     Compiler*            comp          = data->compiler;
105     GenTree*             tree          = *pTree;
106     ShadowParamVarInfo*  shadowVarInfo = pState->comp->gsShadowVarInfo;
107     assert(shadowVarInfo);
108     bool     fIsBlk = false;
109     unsigned lclNum;
110
111     assert(!pState->isAssignSrc || pState->lvAssignDef != (unsigned)-1);
112
113     if (pState->skipNextNode)
114     {
115         pState->skipNextNode = false;
116         return WALK_CONTINUE;
117     }
118
119     switch (tree->OperGet())
120     {
121         // Indirections - look for *p uses and defs
122         case GT_IND:
123         case GT_OBJ:
124         case GT_ARR_ELEM:
125         case GT_ARR_INDEX:
126         case GT_ARR_OFFSET:
127         case GT_FIELD:
128
129             newState.isUnderIndir = true;
130             {
131                 newState.skipNextNode = true; // Don't have to worry about which kind of node we're dealing with
132                 comp->fgWalkTreePre(&tree, comp->gsMarkPtrsAndAssignGroups, (void*)&newState);
133             }
134
135             return WALK_SKIP_SUBTREES;
136
137         // local vars and param uses
138         case GT_LCL_VAR:
139         case GT_LCL_FLD:
140             lclNum = tree->gtLclVarCommon.gtLclNum;
141
142             if (pState->isUnderIndir)
143             {
144                 // The variable is being dereferenced for a read or a write.
145                 comp->lvaTable[lclNum].lvIsPtr = 1;
146             }
147
148             if (pState->isAssignSrc)
149             {
150                 //
151                 // Add lvAssignDef and lclNum to a common assign group
152                 if (shadowVarInfo[pState->lvAssignDef].assignGroup)
153                 {
154                     if (shadowVarInfo[lclNum].assignGroup)
155                     {
156                         // OR both bit vector
157                         shadowVarInfo[pState->lvAssignDef].assignGroup->bitVectOr(shadowVarInfo[lclNum].assignGroup);
158                     }
159                     else
160                     {
161                         shadowVarInfo[pState->lvAssignDef].assignGroup->bitVectSet(lclNum);
162                     }
163
164                     // Point both to the same bit vector
165                     shadowVarInfo[lclNum].assignGroup = shadowVarInfo[pState->lvAssignDef].assignGroup;
166                 }
167                 else if (shadowVarInfo[lclNum].assignGroup)
168                 {
169                     shadowVarInfo[lclNum].assignGroup->bitVectSet(pState->lvAssignDef);
170
171                     // Point both to the same bit vector
172                     shadowVarInfo[pState->lvAssignDef].assignGroup = shadowVarInfo[lclNum].assignGroup;
173                 }
174                 else
175                 {
176                     FixedBitVect* bv = FixedBitVect::bitVectInit(pState->comp->lvaCount, pState->comp);
177
178                     // (shadowVarInfo[pState->lvAssignDef] == NULL && shadowVarInfo[lclNew] == NULL);
179                     // Neither of them has an assign group yet.  Make a new one.
180                     shadowVarInfo[pState->lvAssignDef].assignGroup = bv;
181                     shadowVarInfo[lclNum].assignGroup              = bv;
182                     bv->bitVectSet(pState->lvAssignDef);
183                     bv->bitVectSet(lclNum);
184                 }
185             }
186             return WALK_CONTINUE;
187
188         // Calls - Mark arg variables
189         case GT_CALL:
190
191             newState.isUnderIndir = false;
192             newState.isAssignSrc  = false;
193             {
194                 if (tree->gtCall.gtCallObjp)
195                 {
196                     newState.isUnderIndir = true;
197                     comp->fgWalkTreePre(&tree->gtCall.gtCallObjp, gsMarkPtrsAndAssignGroups, (void*)&newState);
198                 }
199
200                 for (GenTreeArgList* args = tree->gtCall.gtCallArgs; args; args = args->Rest())
201                 {
202                     comp->fgWalkTreePre(&args->Current(), gsMarkPtrsAndAssignGroups, (void*)&newState);
203                 }
204                 for (GenTreeArgList* args = tree->gtCall.gtCallLateArgs; args; args = args->Rest())
205                 {
206                     comp->fgWalkTreePre(&args->Current(), gsMarkPtrsAndAssignGroups, (void*)&newState);
207                 }
208
209                 if (tree->gtCall.gtCallType == CT_INDIRECT)
210                 {
211                     newState.isUnderIndir = true;
212
213                     // A function pointer is treated like a write-through pointer since
214                     // it controls what code gets executed, and so indirectly can cause
215                     // a write to memory.
216                     comp->fgWalkTreePre(&tree->gtCall.gtCallAddr, gsMarkPtrsAndAssignGroups, (void*)&newState);
217                 }
218             }
219             return WALK_SKIP_SUBTREES;
220
221         case GT_ADDR:
222             newState.isUnderIndir = false;
223             // We'll assume p in "**p = " can be vulnerable because by changing 'p', someone
224             // could control where **p stores to.
225             {
226                 comp->fgWalkTreePre(&tree->gtOp.gtOp1, comp->gsMarkPtrsAndAssignGroups, (void*)&newState);
227             }
228             return WALK_SKIP_SUBTREES;
229
230         default:
231             // Assignments - track assign groups and *p defs.
232             if (tree->OperIsAssignment())
233             {
234                 bool isLocVar;
235                 bool isLocFld;
236
237                 if (tree->OperIsBlkOp())
238                 {
239                     // Blk assignments are always handled as if they have implicit indirections.
240                     // TODO-1stClassStructs: improve this.
241                     newState.isUnderIndir = true;
242                     comp->fgWalkTreePre(&tree->gtOp.gtOp1, comp->gsMarkPtrsAndAssignGroups, (void*)&newState);
243
244                     if (tree->OperIsInitBlkOp())
245                     {
246                         newState.isUnderIndir = false;
247                     }
248                     comp->fgWalkTreePre(&tree->gtOp.gtOp2, comp->gsMarkPtrsAndAssignGroups, (void*)&newState);
249                 }
250                 else
251                 {
252                     // Walk dst side
253                     comp->fgWalkTreePre(&tree->gtOp.gtOp1, comp->gsMarkPtrsAndAssignGroups, (void*)&newState);
254
255                     // Now handle src side
256                     isLocVar = tree->gtOp.gtOp1->OperGet() == GT_LCL_VAR;
257                     isLocFld = tree->gtOp.gtOp1->OperGet() == GT_LCL_FLD;
258
259                     if ((isLocVar || isLocFld) && tree->gtOp.gtOp2)
260                     {
261                         lclNum               = tree->gtOp.gtOp1->gtLclVarCommon.gtLclNum;
262                         newState.lvAssignDef = lclNum;
263                         newState.isAssignSrc = true;
264                     }
265
266                     comp->fgWalkTreePre(&tree->gtOp.gtOp2, comp->gsMarkPtrsAndAssignGroups, (void*)&newState);
267                 }
268
269                 return WALK_SKIP_SUBTREES;
270             }
271     }
272
273     return WALK_CONTINUE;
274 }
275
276 /*****************************************************************************
277  * gsFindVulnerableParams
278  * Walk all the trees looking for ptrs, args, assign groups, *p stores, etc.
279  * Then use that info to figure out vulnerable pointers.
280  *
281  * It returns true if it found atleast one vulnerable pointer parameter that
282  * needs to be shadow-copied.
283  */
284
285 bool Compiler::gsFindVulnerableParams()
286 {
287     MarkPtrsInfo info;
288
289     info.comp         = this;
290     info.lvAssignDef  = (unsigned)-1;
291     info.isUnderIndir = false;
292     info.isAssignSrc  = false;
293     info.skipNextNode = false;
294
295     // Walk all the trees setting lvIsWritePtr, lvIsOutgoingArg, lvIsPtr and assignGroup.
296     fgWalkAllTreesPre(gsMarkPtrsAndAssignGroups, &info);
297
298     // Compute has vulnerable at the end of the loop.
299     bool hasOneVulnerable = false;
300
301     // Initialize propagated[v0...vn] = {0}^n, so we can skip the ones propagated through
302     // some assign group.
303     FixedBitVect* propagated = (lvaCount > 0) ? FixedBitVect::bitVectInit(lvaCount, this) : nullptr;
304
305     for (UINT lclNum = 0; lclNum < lvaCount; lclNum++)
306     {
307         LclVarDsc*          varDsc     = &lvaTable[lclNum];
308         ShadowParamVarInfo* shadowInfo = &gsShadowVarInfo[lclNum];
309
310         // If there was an indirection or if unsafe buffer, then we'd call it vulnerable.
311         if (varDsc->lvIsPtr || varDsc->lvIsUnsafeBuffer)
312         {
313             hasOneVulnerable = true;
314         }
315
316         // Now, propagate the info through the assign group (an equivalence class of vars transitively assigned.)
317         if (shadowInfo->assignGroup == nullptr || propagated->bitVectTest(lclNum))
318         {
319             continue;
320         }
321
322         // Propagate lvIsPtr, so that:
323         //   1. Any parameter in the equivalence class can be identified as lvIsPtr and hence shadowed.
324         //   2. Buffers with pointers are placed at lower memory addresses than buffers without pointers.
325         UINT isUnderIndir = varDsc->lvIsPtr;
326
327         // First pass -- find if any variable is vulnerable.
328         FixedBitVect* assignGroup = shadowInfo->assignGroup;
329         for (UINT lclNum = assignGroup->bitVectGetFirst(); lclNum != (unsigned)-1 && !isUnderIndir;
330              lclNum      = assignGroup->bitVectGetNext(lclNum))
331         {
332             isUnderIndir |= lvaTable[lclNum].lvIsPtr;
333         }
334
335         // Vulnerable, so propagate to all members of the equivalence class.
336         if (isUnderIndir)
337         {
338             hasOneVulnerable = true;
339         }
340         // Nothing to propagate.
341         else
342         {
343             continue;
344         }
345
346         // Second pass -- mark all are vulnerable.
347         assert(isUnderIndir);
348         for (UINT lclNum = assignGroup->bitVectGetFirst(); lclNum != (unsigned)-1;
349              lclNum      = assignGroup->bitVectGetNext(lclNum))
350         {
351             lvaTable[lclNum].lvIsPtr = TRUE;
352             propagated->bitVectSet(lclNum);
353         }
354
355 #ifdef DEBUG
356         if (verbose)
357         {
358             printf("Equivalence assign group %s: ", isUnderIndir ? "isPtr " : "");
359             for (UINT lclNum = assignGroup->bitVectGetFirst(); lclNum != (unsigned)-1;
360                  lclNum      = assignGroup->bitVectGetNext(lclNum))
361             {
362                 gtDispLclVar(lclNum, false);
363                 printf(" ");
364             }
365             printf("\n");
366         }
367 #endif
368     }
369
370     return hasOneVulnerable;
371 }
372
373 /*****************************************************************************
374  * gsParamsToShadows
375  * Copy each vulnerable param ptr or buffer to a local shadow copy and replace
376  * uses of the param by the shadow copy
377  */
378 void Compiler::gsParamsToShadows()
379 {
380     // Cache old count since we'll add new variables, and
381     // gsShadowVarInfo will not grow to accomodate the new ones.
382     UINT lvaOldCount = lvaCount;
383
384     // Create shadow copy for each param candidate
385     for (UINT lclNum = 0; lclNum < lvaOldCount; lclNum++)
386     {
387         LclVarDsc* varDsc                  = &lvaTable[lclNum];
388         gsShadowVarInfo[lclNum].shadowCopy = NO_SHADOW_COPY;
389
390         // Only care about params whose values are on the stack
391         if (!ShadowParamVarInfo::mayNeedShadowCopy(varDsc))
392         {
393             continue;
394         }
395
396         if (!varDsc->lvIsPtr && !varDsc->lvIsUnsafeBuffer)
397         {
398             continue;
399         }
400
401         int shadowVar = lvaGrabTemp(false DEBUGARG("shadowVar"));
402         // reload varDsc as lvaGrabTemp may realloc the lvaTable[]
403         varDsc = &lvaTable[lclNum];
404
405         // Copy some info
406
407         var_types type             = varTypeIsSmall(varDsc->TypeGet()) ? TYP_INT : varDsc->TypeGet();
408         lvaTable[shadowVar].lvType = type;
409
410 #ifdef FEATURE_SIMD
411         lvaTable[shadowVar].lvSIMDType            = varDsc->lvSIMDType;
412         lvaTable[shadowVar].lvUsedInSIMDIntrinsic = varDsc->lvUsedInSIMDIntrinsic;
413         if (varDsc->lvSIMDType)
414         {
415             lvaTable[shadowVar].lvExactSize = varDsc->lvExactSize;
416             lvaTable[shadowVar].lvBaseType  = varDsc->lvBaseType;
417         }
418 #endif
419         lvaTable[shadowVar].lvRegStruct = varDsc->lvRegStruct;
420
421         lvaTable[shadowVar].lvAddrExposed     = varDsc->lvAddrExposed;
422         lvaTable[shadowVar].lvDoNotEnregister = varDsc->lvDoNotEnregister;
423 #ifdef DEBUG
424         lvaTable[shadowVar].lvVMNeedsStackAddr = varDsc->lvVMNeedsStackAddr;
425         lvaTable[shadowVar].lvLiveInOutOfHndlr = varDsc->lvLiveInOutOfHndlr;
426         lvaTable[shadowVar].lvLclFieldExpr     = varDsc->lvLclFieldExpr;
427         lvaTable[shadowVar].lvLiveAcrossUCall  = varDsc->lvLiveAcrossUCall;
428 #endif
429         lvaTable[shadowVar].lvVerTypeInfo    = varDsc->lvVerTypeInfo;
430         lvaTable[shadowVar].lvGcLayout       = varDsc->lvGcLayout;
431         lvaTable[shadowVar].lvIsUnsafeBuffer = varDsc->lvIsUnsafeBuffer;
432         lvaTable[shadowVar].lvIsPtr          = varDsc->lvIsPtr;
433
434 #ifdef DEBUG
435         if (verbose)
436         {
437             printf("Var V%02u is shadow param candidate. Shadow copy is V%02u.\n", lclNum, shadowVar);
438         }
439 #endif
440
441         gsShadowVarInfo[lclNum].shadowCopy = shadowVar;
442     }
443
444     // Replace param uses with shadow copy
445     fgWalkAllTreesPre(gsReplaceShadowParams, (void*)this);
446
447     // Now insert code to copy the params to their shadow copy.
448     for (UINT lclNum = 0; lclNum < lvaOldCount; lclNum++)
449     {
450         LclVarDsc* varDsc = &lvaTable[lclNum];
451
452         unsigned shadowVar = gsShadowVarInfo[lclNum].shadowCopy;
453         if (shadowVar == NO_SHADOW_COPY)
454         {
455             continue;
456         }
457
458         var_types type = lvaTable[shadowVar].TypeGet();
459
460         GenTree* src = gtNewLclvNode(lclNum, varDsc->TypeGet());
461         GenTree* dst = gtNewLclvNode(shadowVar, type);
462
463         src->gtFlags |= GTF_DONT_CSE;
464         dst->gtFlags |= GTF_DONT_CSE;
465
466         GenTree* opAssign = nullptr;
467         if (type == TYP_STRUCT)
468         {
469             CORINFO_CLASS_HANDLE clsHnd = varDsc->lvVerTypeInfo.GetClassHandle();
470
471             // We don't need unsafe value cls check here since we are copying the params and this flag
472             // would have been set on the original param before reaching here.
473             lvaSetStruct(shadowVar, clsHnd, false);
474
475             src = gtNewOperNode(GT_ADDR, TYP_BYREF, src);
476             dst = gtNewOperNode(GT_ADDR, TYP_BYREF, dst);
477
478             opAssign                            = gtNewCpObjNode(dst, src, clsHnd, false);
479             lvaTable[shadowVar].lvIsMultiRegArg = lvaTable[lclNum].lvIsMultiRegArg;
480             lvaTable[shadowVar].lvIsMultiRegRet = lvaTable[lclNum].lvIsMultiRegRet;
481         }
482         else
483         {
484             opAssign = gtNewAssignNode(dst, src);
485         }
486         fgEnsureFirstBBisScratch();
487         (void)fgInsertStmtAtBeg(fgFirstBB, fgMorphTree(opAssign));
488     }
489
490     // If the method has "Jmp CalleeMethod", then we need to copy shadow params back to original
491     // params before "jmp" to CalleeMethod.
492     if (compJmpOpUsed)
493     {
494         // There could be more than one basic block ending with a "Jmp" type tail call.
495         // We would have to insert assignments in all such blocks, just before GT_JMP stmnt.
496         for (BasicBlock* block = fgFirstBB; block; block = block->bbNext)
497         {
498             if (block->bbJumpKind != BBJ_RETURN)
499             {
500                 continue;
501             }
502
503             if ((block->bbFlags & BBF_HAS_JMP) == 0)
504             {
505                 continue;
506             }
507
508             for (UINT lclNum = 0; lclNum < info.compArgsCount; lclNum++)
509             {
510                 LclVarDsc* varDsc = &lvaTable[lclNum];
511
512                 unsigned shadowVar = gsShadowVarInfo[lclNum].shadowCopy;
513                 if (shadowVar == NO_SHADOW_COPY)
514                 {
515                     continue;
516                 }
517
518                 GenTree* src = gtNewLclvNode(shadowVar, lvaTable[shadowVar].TypeGet());
519                 GenTree* dst = gtNewLclvNode(lclNum, varDsc->TypeGet());
520
521                 src->gtFlags |= GTF_DONT_CSE;
522                 dst->gtFlags |= GTF_DONT_CSE;
523
524                 GenTree* opAssign = nullptr;
525                 if (varDsc->TypeGet() == TYP_STRUCT)
526                 {
527                     CORINFO_CLASS_HANDLE clsHnd = varDsc->lvVerTypeInfo.GetClassHandle();
528                     src                         = gtNewOperNode(GT_ADDR, TYP_BYREF, src);
529                     dst                         = gtNewOperNode(GT_ADDR, TYP_BYREF, dst);
530
531                     opAssign = gtNewCpObjNode(dst, src, clsHnd, false);
532                 }
533                 else
534                 {
535                     opAssign = gtNewAssignNode(dst, src);
536                 }
537
538                 (void)fgInsertStmtNearEnd(block, fgMorphTree(opAssign));
539             }
540         }
541     }
542 }
543
544 /*****************************************************************************
545  * gsReplaceShadowParams (tree-walk call-back)
546  * Replace all vulnerable param uses by it's shadow copy.
547  */
548
549 Compiler::fgWalkResult Compiler::gsReplaceShadowParams(GenTree** pTree, fgWalkData* data)
550 {
551     Compiler* comp = data->compiler;
552     GenTree*  tree = *pTree;
553     GenTree*  asg  = nullptr;
554
555     if (tree->gtOper == GT_ASG)
556     {
557         asg  = tree;             // "asg" is the assignment tree.
558         tree = tree->gtOp.gtOp1; // "tree" is the local var tree at the left-hand size of the assignment.
559     }
560
561     if (tree->gtOper == GT_LCL_VAR || tree->gtOper == GT_LCL_FLD)
562     {
563         UINT paramNum = tree->gtLclVarCommon.gtLclNum;
564
565         if (!ShadowParamVarInfo::mayNeedShadowCopy(&comp->lvaTable[paramNum]) ||
566             comp->gsShadowVarInfo[paramNum].shadowCopy == NO_SHADOW_COPY)
567         {
568             return WALK_CONTINUE;
569         }
570
571         tree->gtLclVarCommon.SetLclNum(comp->gsShadowVarInfo[paramNum].shadowCopy);
572
573         // In gsParamsToShadows(), we create a shadow var of TYP_INT for every small type param.
574         // Make sure we update the type of the local var tree as well.
575         if (varTypeIsSmall(comp->lvaTable[paramNum].TypeGet()))
576         {
577             tree->gtType = TYP_INT;
578             if (asg)
579             {
580                 // If this is an assignment tree, propagate the type to it as well.
581                 asg->gtType = TYP_INT;
582             }
583         }
584     }
585
586     return WALK_CONTINUE;
587 }