/
StackScheduler.Analyzer.vb
1331 lines (1035 loc) · 58.8 KB
/
StackScheduler.Analyzer.vb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
' Licensed to the .NET Foundation under one or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.
Imports System.Collections.Immutable
Imports System.Runtime.InteropServices
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports TypeKind = Microsoft.CodeAnalysis.TypeKind
Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGen
Partial Friend Class StackScheduler
''' <summary>
''' context of expression evaluation.
''' it will affect inference of stack behavior
''' it will also affect when expressions can be dup-reused
''' Example:
''' Goo(x, ref x) x cannot be duped as it is used in different context
''' </summary>
Private Enum ExprContext
None
Sideeffects
Value
Address
AssignmentTarget
Box
End Enum
''' <summary>
''' Analyzes the tree trying to figure which locals may live on stack. It is
''' a fairly delicate process and must be very familiar with how CodeGen works.
''' It is essentially a part of CodeGen.
'''
''' NOTE: It is always safe to mark a local as not eligible as a stack local
''' so when situation gets complicated we just refuse to schedule and move on.
''' </summary>
Private NotInheritable Class Analyzer
Inherits BoundTreeRewriter
Private ReadOnly _container As Symbol
Private _counter As Integer = 0
Private ReadOnly _evalStack As ArrayBuilder(Of (expression As BoundExpression, context As ExprContext))
Private ReadOnly _debugFriendly As Boolean
Private _context As ExprContext = ExprContext.None
Private _assignmentLocal As BoundLocal = Nothing
Private ReadOnly _locals As New Dictionary(Of LocalSymbol, LocalDefUseInfo)
''' <summary>
''' fake local that represents the eval stack. when we need to ensure that eval
''' stack is not blocked by stack Locals, we record an access to empty.
''' </summary>
Private ReadOnly _empty As DummyLocal
' we need to guarantee same stack patterns at branches and labels. we do that by placing
' a fake dummy local at one end of a branch and force that it is accessible at another.
' if any stack local tries to intervene and misbalance the stack, it will clash with
' the dummy and will be rejected.
Private ReadOnly _dummyVariables As New Dictionary(Of Object, DummyLocal)
Private _recursionDepth As Integer
Private Sub New(container As Symbol,
evalStack As ArrayBuilder(Of ValueTuple(Of BoundExpression, ExprContext)),
debugFriendly As Boolean)
Me._container = container
Me._evalStack = evalStack
Me._debugFriendly = debugFriendly
Me._empty = New DummyLocal(container)
' this is the top of eval stack
DeclareLocal(_empty, 0)
RecordDummyWrite(_empty)
End Sub
Public Shared Function Analyze(
container As Symbol,
node As BoundNode,
debugFriendly As Boolean,
<Out> ByRef locals As Dictionary(Of LocalSymbol, LocalDefUseInfo)) As BoundNode
Dim evalStack = ArrayBuilder(Of ValueTuple(Of BoundExpression, ExprContext)).GetInstance()
Dim analyzer = New Analyzer(container, evalStack, debugFriendly)
Dim rewritten As BoundNode = analyzer.Visit(node)
evalStack.Free()
locals = analyzer._locals
Return rewritten
End Function
Public Overrides Function Visit(node As BoundNode) As BoundNode
Dim result As BoundNode
Dim expr = TryCast(node, BoundExpression)
If expr IsNot Nothing Then
Debug.Assert(expr.Kind <> BoundKind.Label)
result = VisitExpression(expr, ExprContext.Value)
Else
result = VisitStatement(node)
End If
Return result
End Function
Private Function VisitExpressionCore(node As BoundExpression, context As ExprContext) As BoundExpression
If node Is Nothing Then
Me._counter += 1
Return node
End If
Dim prevContext As ExprContext = Me._context
Dim prevStack As Integer = Me.StackDepth()
Me._context = context
' Don not recurse into constant expressions. Their children do Not push any values.
Dim result = If(node.ConstantValueOpt Is Nothing,
DirectCast(MyBase.Visit(node), BoundExpression),
node)
_context = prevContext
_counter += 1
Select Case context
Case ExprContext.Sideeffects
SetStackDepth(prevStack)
Case ExprContext.AssignmentTarget
Exit Select
Case ExprContext.Value, ExprContext.Address, ExprContext.Box
SetStackDepth(prevStack)
PushEvalStack(node, context)
Case Else
Throw ExceptionUtilities.UnexpectedValue(context)
End Select
Return result
End Function
Private Sub PushEvalStack(result As BoundExpression, context As ExprContext)
Debug.Assert(result IsNot Nothing OrElse context = ExprContext.None)
_evalStack.Add((result, context))
End Sub
Private Function StackDepth() As Integer
Return _evalStack.Count
End Function
Private Function EvalStackIsEmpty() As Boolean
Return StackDepth() = 0
End Function
Private Sub SetStackDepth(depth As Integer)
_evalStack.Clip(depth)
End Sub
Private Sub PopEvalStack()
SetStackDepth(_evalStack.Count - 1)
End Sub
Private Function VisitExpression(node As BoundExpression, context As ExprContext) As BoundExpression
Dim result As BoundExpression
_recursionDepth += 1
If _recursionDepth > 1 Then
StackGuard.EnsureSufficientExecutionStack(_recursionDepth)
result = VisitExpressionCore(node, context)
Else
result = VisitExpressionCoreWithStackGuard(node, context)
End If
_recursionDepth -= 1
Return result
End Function
Private Function VisitExpressionCoreWithStackGuard(node As BoundExpression, context As ExprContext) As BoundExpression
Debug.Assert(_recursionDepth = 1)
Try
Dim result = VisitExpressionCore(node, context)
Debug.Assert(_recursionDepth = 1)
Return result
Catch ex As InsufficientExecutionStackException
Throw New CancelledByStackGuardException(ex, node)
End Try
End Function
Protected Overrides Function VisitExpressionWithoutStackGuard(node As BoundExpression) As BoundExpression
Throw ExceptionUtilities.Unreachable
End Function
Public Overrides Function VisitSpillSequence(node As BoundSpillSequence) As BoundNode
Throw ExceptionUtilities.Unreachable
End Function
Private Function VisitStatement(node As BoundNode) As BoundNode
Debug.Assert(node Is Nothing OrElse EvalStackIsEmpty())
Dim origStack = StackDepth()
Dim prevContext As ExprContext = Me._context
Dim result As BoundNode = MyBase.Visit(node)
' prevent cross-statement local optimizations
' when emitting debug-friendly code.
If _debugFriendly Then
EnsureOnlyEvalStack()
End If
Me._context = prevContext
SetStackDepth(origStack)
_counter += 1
Return result
End Function
''' <summary>
''' here we have a case of indirect assignment: *t1 = expr;
''' normally we would need to push t1 and that will cause spilling of t2
'''
''' TODO: an interesting case arises in unused x[i]++ and ++x[i] :
''' we have trees that look like:
'''
''' t1 = &(x[0])
''' t2 = *t1
''' *t1 = t2 + 1
'''
''' t1 = &(x[0])
''' t2 = *t1 + 1
''' *t1 = t2
'''
''' in these cases, we could keep t2 on stack (dev10 does).
''' we are dealing with exactly 2 locals and access them in strict order
''' t1, t2, t1, t2 and we are not using t2 after that.
''' We may consider detecting exactly these cases and pretend that we do not need
''' to push either t1 or t2 in this case.
''' </summary>
Private Function LhsUsesStackWhenAssignedTo(node As BoundNode, context As ExprContext) As Boolean
Debug.Assert(context = ExprContext.AssignmentTarget)
If node Is Nothing Then
Return Nothing
End If
Select Case node.Kind
Case BoundKind.Local, BoundKind.Parameter
Return False
Case BoundKind.FieldAccess
Return Not DirectCast(node, BoundFieldAccess).FieldSymbol.IsShared
Case BoundKind.Sequence
Return LhsUsesStackWhenAssignedTo(DirectCast(node, BoundSequence).ValueOpt, context)
End Select
Return True
End Function
Public Overrides Function VisitBlock(node As BoundBlock) As BoundNode
Debug.Assert(EvalStackIsEmpty(), "entering blocks when evaluation stack is not empty?")
' normally we would not allow stack locals
' when evaluation stack is not empty.
DeclareLocals(node.Locals, 0)
Return MyBase.VisitBlock(node)
End Function
Public Overrides Function VisitSequence(node As BoundSequence) As BoundNode
' Normally we can only use stack for local scheduling if stack is not used for evaluation.
' In a context of a regular block that simply means that eval stack must be empty.
' Sequences, can be entered on a nonempty evaluation stack
' Ex:
' a.b = Seq{var y, y = 1, y} // a is on the stack for the duration of the sequence.
'
' However evaluation stack at the entry cannot be used inside the sequence, so such stack
' works as effective "empty" for locals declared in sequence.
' Therefore sequence locals can be stack scheduled at same stack as at the entry to the sequence.
' it may seem attractive to relax the stack requirement to be:
' "all uses must agree on stack depth".
' The following example illustrates a case where x is safely used at "declarationStack + 1"
' Ex:
' Seq{var x; y.a = Seq{x = 1; x}; y} // x is used while y is on the eval stack
'
' It is, however not safe assumption in general since eval stack may be accessed between usages.
' Ex:
' Seq{var x; y.a = Seq{x = 1; x}; y.z = x; y} // x blocks access to y
'
'
' ---------------------------------------------------
' NOTE: The following is not implemented in VB
'
' There is one case where we want to tweak the "use at declaration stack" rule - in the case of
' compound assignment that involves ByRef operand captures (like: x[y]++ ) .
'
' Those cases produce specific sequences of the shapes:
'
' prefix: Seq{var temp, ref operand; operand initializers; *operand = Seq{temp = (T)(operand + 1); temp;} result: temp}
' postfix: Seq{var temp, ref operand; operand initializers; *operand = Seq{temp = operand; ; (T)(temp + 1);} result: temp}
'
' 1) temp is used as the result of the sequence (and that is the only reason why it is declared in the outer sequence).
' 2) all side-effects except the last one do not use the temp.
' 3) last side-effect is an indirect assignment of a sequence (and target does not involve the temp).
'
' Note that in a case of side-effects context, the result value will be ignored and therefore
' all usages of the nested temp will be confined to the nested sequence that is executed at +1 stack.
'
' We will detect such case and indicate +1 as the desired stack depth at local accesses.
' ---------------------------------------------------
'
Dim declarationStack As Integer = Me.StackDepth()
Dim locals = node.Locals
If Not locals.IsEmpty Then
If Me._context = ExprContext.Sideeffects Then
DeclareLocals(locals, declarationStack)
' ---------------------------------------------------
' NOTE: not implemented workaround from above
' foreach (var local in locals)
' {
' if (IsNestedLocalOfCompoundOperator(local, node))
' {
' // special case
' DeclareLocal(local, declarationStack + 1);
' }
' else
' {
' DeclareLocal(local, declarationStack);
' }
' }
' }
' ---------------------------------------------------
Else
DeclareLocals(locals, declarationStack)
End If
End If
' rewrite operands
Dim origContext As ExprContext = Me._context
Dim sideeffects As ImmutableArray(Of BoundExpression) = node.SideEffects
Dim rewrittenSideeffects As ArrayBuilder(Of BoundExpression) = Nothing
If Not sideeffects.IsDefault Then
For i = 0 To sideeffects.Length - 1
Dim sideeffect As BoundExpression = sideeffects(i)
Dim rewrittenSideeffect As BoundExpression = Me.VisitExpression(sideeffect, ExprContext.Sideeffects)
If rewrittenSideeffects Is Nothing AndAlso rewrittenSideeffect IsNot sideeffect Then
rewrittenSideeffects = ArrayBuilder(Of BoundExpression).GetInstance()
rewrittenSideeffects.AddRange(sideeffects, i)
End If
If rewrittenSideeffects IsNot Nothing Then
rewrittenSideeffects.Add(rewrittenSideeffect)
End If
Next
End If
Dim value As BoundExpression = Me.VisitExpression(node.ValueOpt, origContext)
Return node.Update(node.Locals,
If(rewrittenSideeffects IsNot Nothing, rewrittenSideeffects.ToImmutableAndFree(), sideeffects),
value, node.Type)
End Function
#If False Then
'// detect a pattern used in compound operators
'// where a temp is declared in the outer sequence
'// only because it must be returned, otherwise all uses are
'// confined to the nested sequence that is indirectly assigned (and therefore has +1 stack)
'// in such case the desired stack for this local is +1
'private bool IsNestedLocalOfCompoundOperator(LocalSymbol local, BoundSequence node)
'{
' var value = node.Value;
' // local must be used as the value of the sequence.
' if (value != null && value.Kind == BoundKind.Local && ((BoundLocal)value).LocalSymbol == local)
' {
' var sideeffects = node.SideEffects;
' var lastSideeffect = sideeffects.LastOrDefault();
' if (lastSideeffect != null)
' {
' // last side-effect must be an indirect assignment of a sequence.
' if (lastSideeffect.Kind == BoundKind.AssignmentOperator)
' {
' var assignment = (BoundAssignmentOperator)lastSideeffect;
' if (IsIndirectAssignment(assignment) &&
' assignment.Right.Kind == BoundKind.Sequence)
' {
' // and no other side-effects should use the variable
' var localUsedWalker = new LocalUsedWalker(local);
' for (int i = 0; i < sideeffects.Count - 1; i++)
' {
' if (localUsedWalker.IsLocalUsedIn(sideeffects[i]))
' {
' return false;
' }
' }
' // and local is not used on the left of the assignment
' // (extra check, but better be safe)
' if (localUsedWalker.IsLocalUsedIn(assignment.Left))
' {
' return false;
' }
' // it should be used somewhere
' Debug.Assert(localUsedWalker.IsLocalUsedIn(assignment.Right), "who assigns the temp?");
' return true;
' }
' }
' }
' }
' return false;
'}
'private class LocalUsedWalker : BoundTreeWalker
'{
' private readonly LocalSymbol local;
' private bool found;
' internal LocalUsedWalker(LocalSymbol local)
' {
' this.local = local;
' }
' public bool IsLocalUsedIn(BoundExpression node)
' {
' this.found = false;
' this.Visit(node);
' return found;
' }
' public override BoundNode Visit(BoundNode node)
' {
' if (!found)
' {
' return base.Visit(node);
' }
' return null;
' }
' public override BoundNode VisitLocal(BoundLocal node)
' {
' if (node.LocalSymbol == local)
' {
' this.found = true;
' }
' return null;
' }
'}
#End If
Public Overrides Function VisitExpressionStatement(node As BoundExpressionStatement) As BoundNode
Return node.Update(Me.VisitExpression(node.Expression, ExprContext.Sideeffects))
End Function
Public Overrides Function VisitLocal(node As BoundLocal) As BoundNode
If node.ConstantValueOpt Is Nothing Then
Select Case Me._context
Case ExprContext.Address
If node.LocalSymbol.IsByRef Then
RecordVarRead(node.LocalSymbol)
Else
RecordVarRef(node.LocalSymbol)
End If
Case ExprContext.AssignmentTarget
Debug.Assert(Me._assignmentLocal Is Nothing)
' actual assignment will happen later, after Right is evaluated
' just remember what we are assigning to.
Me._assignmentLocal = node
Case ExprContext.Sideeffects
' do nothing
Case ExprContext.Value,
ExprContext.Box
RecordVarRead(node.LocalSymbol)
End Select
End If
Return MyBase.VisitLocal(node)
End Function
Public Overrides Function VisitReferenceAssignment(node As BoundReferenceAssignment) As BoundNode
' Visit a local in context of regular assignment
Dim left = DirectCast(VisitExpression(node.ByRefLocal, ExprContext.AssignmentTarget), BoundLocal)
Dim storedAssignmentLocal = Me._assignmentLocal
Me._assignmentLocal = Nothing
' Visit a l-value expression in context of 'address'
Dim right As BoundExpression = VisitExpression(node.LValue, ExprContext.Address)
' record the Write to the local
Debug.Assert(storedAssignmentLocal IsNot Nothing)
' this assert will fire if code relies on implicit CLR coercions
' - i.e assigns int value to a short local.
' in that case we should force lhs to be a real local
Debug.Assert(node.ByRefLocal.Type.IsSameTypeIgnoringAll(node.LValue.Type),
"cannot use stack when assignment involves implicit coercion of the value")
RecordVarWrite(storedAssignmentLocal.LocalSymbol)
Return node.Update(left, right, node.IsLValue, node.Type)
End Function
Public Overrides Function VisitAssignmentOperator(node As BoundAssignmentOperator) As BoundNode
Dim isIndirect As Boolean = IsIndirectAssignment(node)
Dim left As BoundExpression = VisitExpression(node.Left,
If(isIndirect,
ExprContext.Address,
ExprContext.AssignmentTarget))
' must delay recording a write until after RHS is evaluated
Dim storedAssignmentLocal = Me._assignmentLocal
Me._assignmentLocal = Nothing
Debug.Assert(Me._context <> ExprContext.AssignmentTarget, "assignment expression cannot be a target of another assignment")
' Left on the right should be Nothing by this time
Debug.Assert(node.LeftOnTheRightOpt Is Nothing)
' Do not visit "Left on the right"
'Me.counter += 1
Dim rhsContext As ExprContext
If Me._context = ExprContext.Address Then
' we need the address of rhs so we cannot have it on the stack.
rhsContext = ExprContext.Address
Else
Debug.Assert(Me._context = ExprContext.Value OrElse
Me._context = ExprContext.Box OrElse
Me._context = ExprContext.Sideeffects, "assignment expression cannot be a target of another assignment")
' we only need a value of rhs, so if otherwise possible it can be a stack value.
rhsContext = ExprContext.Value
End If
Dim right As BoundExpression = node.Right
' if right is a struct ctor, it may be optimized into in-place call
' Such call will push the receiver ref before the arguments
' so we need to ensure that arguments cannot use stack temps
Dim leftType As TypeSymbol = left.Type
Dim mayPushReceiver As Boolean = False
If right.Kind = BoundKind.ObjectCreationExpression Then
Dim ctor = DirectCast(right, BoundObjectCreationExpression).ConstructorOpt
If ctor IsNot Nothing AndAlso ctor.ParameterCount <> 0 Then
mayPushReceiver = True
End If
End If
If mayPushReceiver Then
'push unknown value just to prevent access to stack locals.
PushEvalStack(Nothing, ExprContext.None)
End If
right = VisitExpression(node.Right, rhsContext)
If mayPushReceiver Then
PopEvalStack()
End If
' if assigning to a local, now it is the time to record the Write
If storedAssignmentLocal IsNot Nothing Then
' this assert will fire if code relies on implicit CLR coercions
' - i.e assigns int value to a short local.
' in that case we should force lhs to be a real local
Debug.Assert(node.Left.Type.IsSameTypeIgnoringAll(node.Right.Type),
"cannot use stack when assignment involves implicit coercion of the value")
Debug.Assert(Not isIndirect, "indirect assignment is a read, not a write")
RecordVarWrite(storedAssignmentLocal.LocalSymbol)
End If
Return node.Update(left, Nothing, right, node.SuppressObjectClone, node.Type)
End Function
''' <summary>
''' VB uses a special node to assign references.
''' BoundAssignment is used only to assign values.
''' therefore an indirect assignment may only happen if lhs is a reference
''' </summary>
Private Shared Function IsIndirectAssignment(node As BoundAssignmentOperator) As Boolean
Return IsByRefVariable(node.Left)
End Function
Private Shared Function IsByRefVariable(node As BoundExpression) As Boolean
Select Case node.Kind
Case BoundKind.Parameter
Return DirectCast(node, BoundParameter).ParameterSymbol.IsByRef
Case BoundKind.Local
Return DirectCast(node, BoundLocal).LocalSymbol.IsByRef
Case BoundKind.Call
Return DirectCast(node, BoundCall).Method.ReturnsByRef
Case BoundKind.Sequence
Debug.Assert(Not IsByRefVariable(DirectCast(node, BoundSequence).ValueOpt))
Return False
Case BoundKind.PseudoVariable
Return True
Case BoundKind.ReferenceAssignment
Return True
Case BoundKind.ValueTypeMeReference
Return True
Case BoundKind.ModuleVersionId,
BoundKind.InstrumentationPayloadRoot
' same as static fields
Return False
Case BoundKind.FieldAccess,
BoundKind.ArrayAccess
' fields are never byref
Return False
Case Else
Throw ExceptionUtilities.UnexpectedValue(node.Kind)
End Select
End Function
Public Overrides Function VisitCall(node As BoundCall) As BoundNode
Dim receiver = node.ReceiverOpt
' matches or a bit stronger than EmitReceiverRef
' if there are any doubts that receiver is a ref type,
' assume we will need an address (that will prevent scheduling of receiver).
If Not node.Method.IsShared Then
Dim receiverType = receiver.Type
Dim context As ExprContext
If receiverType.IsReferenceType Then
If (receiverType.IsTypeParameter()) Then
' type param receiver that we statically know Is a reference will be boxed
context = ExprContext.Box
Else
' reference receivers will be used as values
context = ExprContext.Value
End If
Else
' everything else will get an address taken
context = ExprContext.Address
End If
receiver = VisitExpression(receiver, context)
Else
Me._counter += 1
Debug.Assert(receiver Is Nothing OrElse receiver.Kind = BoundKind.TypeExpression)
End If
Dim method As MethodSymbol = node.Method
Dim rewrittenArguments As ImmutableArray(Of BoundExpression) = VisitArguments(node.Arguments, method.Parameters)
Debug.Assert(node.MethodGroupOpt Is Nothing)
Return node.Update(
method,
node.MethodGroupOpt,
receiver,
rewrittenArguments,
node.DefaultArguments,
node.ConstantValueOpt,
isLValue:=node.IsLValue,
suppressObjectClone:=node.SuppressObjectClone,
type:=node.Type)
End Function
Private Function VisitArguments(arguments As ImmutableArray(Of BoundExpression), parameters As ImmutableArray(Of ParameterSymbol)) As ImmutableArray(Of BoundExpression)
Debug.Assert(Not arguments.IsDefault)
Debug.Assert(Not parameters.IsDefault)
' If this is a varargs method then there will be one additional argument for the __arglist().
Debug.Assert(arguments.Length = parameters.Length OrElse arguments.Length = parameters.Length + 1)
Dim rewrittenArguments As ArrayBuilder(Of BoundExpression) = Nothing
For i = 0 To arguments.Length - 1
' Treat the __arglist() as a value parameter.
Dim context As ExprContext = If(i = parameters.Length OrElse Not parameters(i).IsByRef, ExprContext.Value, ExprContext.Address)
Dim arg As BoundExpression = arguments(i)
Dim rewrittenArg As BoundExpression = VisitExpression(arg, context)
If rewrittenArguments Is Nothing AndAlso arg IsNot rewrittenArg Then
rewrittenArguments = ArrayBuilder(Of BoundExpression).GetInstance()
rewrittenArguments.AddRange(arguments, i)
End If
If rewrittenArguments IsNot Nothing Then
rewrittenArguments.Add(rewrittenArg)
End If
Next
Return If(rewrittenArguments IsNot Nothing, rewrittenArguments.ToImmutableAndFree, arguments)
End Function
Public Overrides Function VisitObjectCreationExpression(node As BoundObjectCreationExpression) As BoundNode
Dim constructor As MethodSymbol = node.ConstructorOpt
Debug.Assert(constructor IsNot Nothing OrElse node.Arguments.Length = 0)
Dim rewrittenArguments As ImmutableArray(Of BoundExpression) = If(constructor Is Nothing, node.Arguments,
VisitArguments(node.Arguments, constructor.Parameters))
Debug.Assert(node.InitializerOpt Is Nothing)
Me._counter += 1
Return node.Update(constructor, rewrittenArguments, node.DefaultArguments, Nothing, node.Type)
End Function
Public Overrides Function VisitArrayAccess(node As BoundArrayAccess) As BoundNode
' regardless of purpose, array access visits its children as values
' TODO: do we need to save/restore old context here?
Dim oldContext = Me._context
Me._context = ExprContext.Value
Dim result As BoundNode = MyBase.VisitArrayAccess(node)
Me._context = oldContext
Return result
End Function
Public Overrides Function VisitFieldAccess(node As BoundFieldAccess) As BoundNode
Dim field As FieldSymbol = node.FieldSymbol
Dim receiver As BoundExpression = node.ReceiverOpt
' if there are any doubts that receiver is a ref type, assume we will
' need an address. (that will prevent scheduling of receiver).
If Not field.IsShared Then
If receiver.Type.IsTypeParameter Then
' type parameters must be boxed to access fields.
receiver = VisitExpression(receiver, ExprContext.Box)
Else
' need address when assigning to a field and receiver is not a reference
' when accessing a field of a struct unless we only need Value and Value is preferred.
If receiver.Type.IsValueType AndAlso
(_context = ExprContext.AssignmentTarget OrElse
_context = ExprContext.Address OrElse
CodeGenerator.FieldLoadMustUseRef(receiver)) Then
receiver = VisitExpression(receiver, ExprContext.Address)
Else
receiver = VisitExpression(receiver, ExprContext.Value)
End If
End If
Else
Me._counter += 1
receiver = Nothing
End If
Return node.Update(receiver, field, node.IsLValue, node.SuppressVirtualCalls, constantsInProgressOpt:=Nothing, node.Type)
End Function
Public Overrides Function VisitLabelStatement(node As BoundLabelStatement) As BoundNode
RecordLabel(node.Label)
Return MyBase.VisitLabelStatement(node)
End Function
Public Overrides Function VisitGotoStatement(node As BoundGotoStatement) As BoundNode
Dim result As BoundNode = MyBase.VisitGotoStatement(node)
RecordBranch(node.Label)
Return result
End Function
Public Overrides Function VisitConditionalGoto(node As BoundConditionalGoto) As BoundNode
Dim result As BoundNode = MyBase.VisitConditionalGoto(node)
Me.PopEvalStack() ' condition gets consumed
RecordBranch(node.Label)
Return result
End Function
Public Overrides Function VisitBinaryConditionalExpression(node As BoundBinaryConditionalExpression) As BoundNode
Dim origStack = Me.StackDepth
Dim testExpression As BoundExpression = DirectCast(Me.Visit(node.TestExpression), BoundExpression)
Debug.Assert(node.ConvertedTestExpression Is Nothing)
' Me.counter += 1 '' This child is not visited
Debug.Assert(node.TestExpressionPlaceholder Is Nothing)
' Me.counter += 1 '' This child is not visited
' implicit branch here
Dim cookie As Object = GetStackStateCookie()
Me.SetStackDepth(origStack) ' else expression is evaluated with original stack
Dim elseExpression As BoundExpression = DirectCast(Me.Visit(node.ElseExpression), BoundExpression)
' implicit label here
EnsureStackState(cookie)
Return node.Update(testExpression, Nothing, Nothing, elseExpression, node.ConstantValueOpt, node.Type)
End Function
Public Overrides Function VisitTernaryConditionalExpression(node As BoundTernaryConditionalExpression) As BoundNode
Dim origStack As Integer = Me.StackDepth
Dim condition = DirectCast(Me.Visit(node.Condition), BoundExpression)
Dim cookie As Object = GetStackStateCookie() ' implicit goto here
Me.SetStackDepth(origStack) ' consequence is evaluated with original stack
Dim whenTrue = DirectCast(Me.Visit(node.WhenTrue), BoundExpression)
EnsureStackState(cookie) ' implicit label here
Me.SetStackDepth(origStack) ' alternative is evaluated with original stack
Dim whenFalse = DirectCast(Me.Visit(node.WhenFalse), BoundExpression)
EnsureStackState(cookie) ' implicit label here
Return node.Update(condition, whenTrue, whenFalse, node.ConstantValueOpt, node.Type)
End Function
Public Overrides Function VisitLoweredConditionalAccess(node As BoundLoweredConditionalAccess) As BoundNode
If Not node.ReceiverOrCondition.Type.IsBooleanType() Then
' We may need to load a reference to the receiver, or may need to
' reload it after the null check. This won't work well
' with a stack local.
EnsureOnlyEvalStack()
End If
Dim origStack = StackDepth()
Dim receiverOrCondition = DirectCast(Me.Visit(node.ReceiverOrCondition), BoundExpression)
Dim cookie = GetStackStateCookie() ' implicit branch here
' access Is evaluated with original stack
' (this Is Not entirely true, codegen will keep receiver on the stack, but that Is irrelevant here)
Me.SetStackDepth(origStack)
Dim whenNotNull = DirectCast(Me.Visit(node.WhenNotNull), BoundExpression)
EnsureStackState(cookie) ' implicit label here
Dim whenNull As BoundExpression = Nothing
If node.WhenNullOpt IsNot Nothing Then
Me.SetStackDepth(origStack)
whenNull = DirectCast(Me.Visit(node.WhenNullOpt), BoundExpression)
EnsureStackState(cookie) ' implicit label here
End If
Return node.Update(receiverOrCondition, node.CaptureReceiver, node.PlaceholderId, whenNotNull, whenNull, node.Type)
End Function
Public Overrides Function VisitConditionalAccessReceiverPlaceholder(node As BoundConditionalAccessReceiverPlaceholder) As BoundNode
Return MyBase.VisitConditionalAccessReceiverPlaceholder(node)
End Function
Public Overrides Function VisitComplexConditionalAccessReceiver(node As BoundComplexConditionalAccessReceiver) As BoundNode
EnsureOnlyEvalStack()
Dim origStack As Integer = Me.StackDepth
Me.PushEvalStack(Nothing, ExprContext.None)
Dim cookie As Object = GetStackStateCookie() ' implicit goto here
Me.SetStackDepth(origStack) ' consequence is evaluated with original stack
Dim valueTypeReceiver = DirectCast(Me.VisitExpression(node.ValueTypeReceiver, Me._context), BoundExpression)
EnsureStackState(cookie) ' implicit label here
Me.SetStackDepth(origStack) ' alternative is evaluated with original stack
Dim referenceTypeReceiver = DirectCast(Me.VisitExpression(node.ReferenceTypeReceiver, Me._context), BoundExpression)
EnsureStackState(cookie) ' implicit label here
Return node.Update(valueTypeReceiver, referenceTypeReceiver, node.Type)
End Function
Public Overrides Function VisitBinaryOperator(node As BoundBinaryOperator) As BoundNode
' Do not blow the stack due to a deep recursion on the left.
Dim child As BoundExpression = node.Left
If child.Kind <> BoundKind.BinaryOperator OrElse child.ConstantValueOpt IsNot Nothing Then
Return VisitBinaryOperatorSimple(node)
End If
Dim stack = ArrayBuilder(Of BoundBinaryOperator).GetInstance()
stack.Push(node)
Dim binary As BoundBinaryOperator = DirectCast(child, BoundBinaryOperator)
Do
stack.Push(binary)
child = binary.Left
If child.Kind <> BoundKind.BinaryOperator OrElse child.ConstantValueOpt IsNot Nothing Then
Exit Do
End If
binary = DirectCast(child, BoundBinaryOperator)
Loop
Dim prevStack As Integer = Me.StackDepth()
Dim left = DirectCast(Me.Visit(child), BoundExpression)
Do
binary = stack.Pop()
' Short-circuit operators need to emulate implicit branch/label
Dim isLogical As Boolean
Dim cookie As Object = Nothing
Select Case (binary.OperatorKind And BinaryOperatorKind.OpMask)
Case BinaryOperatorKind.AndAlso, BinaryOperatorKind.OrElse
isLogical = True
' implicit branch here
cookie = GetStackStateCookie()
Me.SetStackDepth(prevStack) ' right is evaluated with original stack
Case Else
isLogical = False
End Select
Dim right = DirectCast(Me.Visit(binary.Right), BoundExpression)
If isLogical Then
' implicit label here
EnsureStackState(cookie)
End If
Dim type As TypeSymbol = Me.VisitType(binary.Type)
left = binary.Update(binary.OperatorKind, left, right, binary.Checked, binary.ConstantValueOpt, type)
If stack.Count = 0 Then
Exit Do
End If
_counter += 1
SetStackDepth(prevStack)
PushEvalStack(node, ExprContext.Value)
Loop
Debug.Assert(binary Is node)
stack.Free()
Return left
End Function
Private Function VisitBinaryOperatorSimple(node As BoundBinaryOperator) As BoundNode
Select Case (node.OperatorKind And BinaryOperatorKind.OpMask)
Case BinaryOperatorKind.AndAlso, BinaryOperatorKind.OrElse
' Short-circuit operators need to emulate implicit branch/label
Dim origStack = Me.StackDepth
Dim left As BoundExpression = DirectCast(Me.Visit(node.Left), BoundExpression)
' implicit branch here
Dim cookie As Object = GetStackStateCookie()
Me.SetStackDepth(origStack) ' right is evaluated with original stack
Dim right As BoundExpression = DirectCast(Me.Visit(node.Right), BoundExpression)
' implicit label here
EnsureStackState(cookie)
Return node.Update(node.OperatorKind, left, right, node.Checked, node.ConstantValueOpt, node.Type)
Case Else
Return MyBase.VisitBinaryOperator(node)
End Select
End Function
Public Overrides Function VisitUnaryOperator(node As BoundUnaryOperator) As BoundNode
' checked(-x) is emitted as "0 - x"