File: CodeGen\EmitStatement.vb
Web Access
Project: src\src\Compilers\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.vbproj (Microsoft.CodeAnalysis.VisualBasic)
' 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.Reflection.Metadata
Imports System.Runtime.InteropServices
Imports Microsoft.CodeAnalysis.CodeGen
Imports Microsoft.CodeAnalysis.Collections
Imports Microsoft.CodeAnalysis.Emit
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
 
Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGen
 
    Partial Friend Class CodeGenerator
        Private Sub EmitStatement(statement As BoundStatement)
            Select Case statement.Kind
 
                Case BoundKind.Block
                    EmitBlock(DirectCast(statement, BoundBlock))
 
                Case BoundKind.SequencePoint
                    EmitSequencePointStatement(DirectCast(statement, BoundSequencePoint))
 
                Case BoundKind.SequencePointWithSpan
                    EmitSequencePointStatement(DirectCast(statement, BoundSequencePointWithSpan))
 
                Case BoundKind.ExpressionStatement
                    EmitExpression((DirectCast(statement, BoundExpressionStatement)).Expression, False)
 
                Case BoundKind.NoOpStatement
                    EmitNoOpStatement(DirectCast(statement, BoundNoOpStatement))
 
                Case BoundKind.StatementList
                    Dim list = DirectCast(statement, BoundStatementList)
                    Dim n As Integer = list.Statements.Length
                    For i = 0 To n - 1
                        EmitStatement(list.Statements(i))
                    Next
 
                Case BoundKind.ReturnStatement
                    EmitReturnStatement(DirectCast(statement, BoundReturnStatement))
 
                Case BoundKind.ThrowStatement
                    EmitThrowStatement(DirectCast(statement, BoundThrowStatement))
 
                Case BoundKind.GotoStatement
                    EmitGotoStatement(DirectCast(statement, BoundGotoStatement))
 
                Case BoundKind.LabelStatement
                    EmitLabelStatement(DirectCast(statement, BoundLabelStatement))
 
                Case BoundKind.ConditionalGoto
                    EmitConditionalGoto(DirectCast(statement, BoundConditionalGoto))
 
                Case BoundKind.TryStatement
                    EmitTryStatement(DirectCast(statement, BoundTryStatement))
 
                Case BoundKind.SelectStatement
                    EmitSelectStatement(DirectCast(statement, BoundSelectStatement))
 
                Case BoundKind.UnstructuredExceptionOnErrorSwitch
                    EmitUnstructuredExceptionOnErrorSwitch(DirectCast(statement, BoundUnstructuredExceptionOnErrorSwitch))
 
                Case BoundKind.UnstructuredExceptionResumeSwitch
                    EmitUnstructuredExceptionResumeSwitch(DirectCast(statement, BoundUnstructuredExceptionResumeSwitch))
 
                Case BoundKind.StateMachineScope
                    EmitStateMachineScope(DirectCast(statement, BoundStateMachineScope))
 
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(statement.Kind)
            End Select
 
#If DEBUG Then
            If Me._stackLocals Is Nothing OrElse Not Me._stackLocals.Any Then
                _builder.AssertStackEmpty()
            End If
#End If
        End Sub
 
        Private Function EmitStatementAndCountInstructions(statement As BoundStatement) As Integer
            Dim n = _builder.InstructionsEmitted
            EmitStatement(statement)
            Return _builder.InstructionsEmitted - n
        End Function
 
        Private Sub EmitNoOpStatement(statement As BoundNoOpStatement)
            Select Case statement.Flavor
                Case NoOpStatementFlavor.Default
                    If _ilEmitStyle = ILEmitStyle.Debug Then
                        _builder.EmitOpCode(ILOpCode.Nop)
                    End If
 
                Case NoOpStatementFlavor.AwaitYieldPoint
                    Debug.Assert((_asyncYieldPoints Is Nothing) = (_asyncResumePoints Is Nothing))
                    If _asyncYieldPoints Is Nothing Then
                        _asyncYieldPoints = ArrayBuilder(Of Integer).GetInstance
                        _asyncResumePoints = ArrayBuilder(Of Integer).GetInstance
                    End If
                    Debug.Assert(_asyncYieldPoints.Count = _asyncResumePoints.Count)
                    _asyncYieldPoints.Add(_builder.AllocateILMarker())
 
                Case NoOpStatementFlavor.AwaitResumePoint
                    Debug.Assert(_asyncYieldPoints IsNot Nothing)
                    Debug.Assert(_asyncResumePoints IsNot Nothing)
                    Debug.Assert((_asyncYieldPoints.Count - 1) = _asyncResumePoints.Count)
                    _asyncResumePoints.Add(_builder.AllocateILMarker())
 
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(statement.Flavor)
            End Select
        End Sub
 
        Private Sub EmitTryStatement(statement As BoundTryStatement, Optional emitCatchesOnly As Boolean = False)
            Debug.Assert(Not statement.CatchBlocks.IsDefault)
 
            ' Stack must be empty at beginning of try block.
            _builder.AssertStackEmpty()
 
            ' IL requires catches and finally block to be distinct try
            ' blocks so if the source contained both a catch and
            ' a finally, nested scopes are emitted.
            Dim emitNestedScopes As Boolean = (Not emitCatchesOnly AndAlso (statement.CatchBlocks.Length > 0) AndAlso (statement.FinallyBlockOpt IsNot Nothing))
            _builder.OpenLocalScope(ScopeType.TryCatchFinally)
            _builder.OpenLocalScope(ScopeType.Try)
 
            Me._tryNestingLevel += 1
 
            If emitNestedScopes Then
                EmitTryStatement(statement, emitCatchesOnly:=True)
            Else
                EmitBlock(statement.TryBlock)
            End If
 
            Debug.Assert(Me._tryNestingLevel > 0)
            Me._tryNestingLevel -= 1
 
            ' close Try scope
            _builder.CloseLocalScope()
 
            If Not emitNestedScopes Then
                For Each catchBlock In statement.CatchBlocks
                    EmitCatchBlock(catchBlock)
                Next
            End If
 
            If Not emitCatchesOnly AndAlso (statement.FinallyBlockOpt IsNot Nothing) Then
                _builder.OpenLocalScope(ScopeType.Finally)
                EmitBlock(statement.FinallyBlockOpt)
                _builder.CloseLocalScope()
            End If
 
            _builder.CloseLocalScope()
 
            If Not emitCatchesOnly AndAlso statement.ExitLabelOpt IsNot Nothing Then
                _builder.MarkLabel(statement.ExitLabelOpt)
            End If
        End Sub
 
        'The interesting part in the following method is the support for exception filters. 
        '=== Example:
        '
        'Try
        '   <SomeCode>
        'Catch ex as NullReferenceException When ex.Message isnot Nothing
        '   <Handler>
        'End Try
        '
        'gets emitted as something like ===>
        '
        'Try
        '   <SomeCode>
        'Filter 
        '    Condition    ' starts with exception on the stack
        '   Dim temp As NullReferenceException = TryCast(Pop, NullReferenceException)
        '         if  temp is Nothing
        '              Push 0
        '         Else
        '              ex = temp
        '              Push if ((ex.Message isnot Nothing), 1, 0)
        '         End If
        '    End Condition   ' leaves 1 or 0 on the stack
        '    Handler             ' gets called after finalization of nested exception frames if condition above produced 1
        '         <Handler>
        '    End Handler
        'End Try
        Private Sub EmitCatchBlock(catchBlock As BoundCatchBlock)
            Dim oldCatchBlock = _currentCatchBlock
            _currentCatchBlock = catchBlock
 
            Dim typeCheckFailedLabel As Object = Nothing
            Dim exceptionSource = catchBlock.ExceptionSourceOpt
 
            Dim exceptionType As Cci.ITypeReference
 
            If exceptionSource IsNot Nothing Then
                exceptionType = Me._module.Translate(exceptionSource.Type, exceptionSource.Syntax, _diagnostics)
            Else
                ' if type is not specified it is assumed to be System.Exception
                exceptionType = Me._module.Translate(Me._module.Compilation.GetWellKnownType(WellKnownType.System_Exception), catchBlock.Syntax, _diagnostics)
            End If
 
            ' exception on stack
            _builder.AdjustStack(1)
 
            If catchBlock.ExceptionFilterOpt IsNot Nothing AndAlso catchBlock.ExceptionFilterOpt.Kind = BoundKind.UnstructuredExceptionHandlingCatchFilter Then
                ' This is a special catch created for Unstructured Exception Handling
                Debug.Assert(catchBlock.LocalOpt Is Nothing)
                Debug.Assert(exceptionSource Is Nothing)
 
                '
                ' Generate the OnError filter.
                '
                ' The On Error filter catches an exception when a handler is active and the method
                ' isn't currently in the process of handling an earlier error.  We know the method
                ' is handling an earlier error when we have a valid Resume target.
                '
                ' The filter expression is the equivalent of:
                '
                '     Catch e When (TypeOf e Is Exception) And (ActiveHandler <> 0) And (ResumeTarget = 0)
                '
                Dim filter = DirectCast(catchBlock.ExceptionFilterOpt, BoundUnstructuredExceptionHandlingCatchFilter)
 
                _builder.OpenLocalScope(ScopeType.Filter)
 
                'Determine if the exception object is or inherits from System.Exception
                _builder.EmitOpCode(ILOpCode.Isinst)
                _builder.EmitToken(exceptionType, catchBlock.Syntax, _diagnostics)
                _builder.EmitOpCode(ILOpCode.Ldnull)
                _builder.EmitOpCode(ILOpCode.Cgt_un)
 
                ' Calculate ActiveHandler <> 0
                EmitLocalLoad(filter.ActiveHandlerLocal, used:=True)
                _builder.EmitIntConstant(0)
                _builder.EmitOpCode(ILOpCode.Cgt_un)
 
                ' AND the values together.
                _builder.EmitOpCode(ILOpCode.And)
 
                ' Calculate ResumeTarget = 0
                EmitLocalLoad(filter.ResumeTargetLocal, used:=True)
                _builder.EmitIntConstant(0)
                _builder.EmitOpCode(ILOpCode.Ceq)
 
                ' AND the values together.
                _builder.EmitOpCode(ILOpCode.And)
 
                ' Now we are starting the actual handler
                _builder.MarkFilterConditionEnd()
 
                _builder.EmitOpCode(ILOpCode.Castclass)
                _builder.EmitToken(exceptionType, catchBlock.Syntax, _diagnostics)
 
                If ShouldNoteProjectErrors() Then
                    EmitSetProjectError(catchBlock.Syntax, catchBlock.ErrorLineNumberOpt)
                Else
                    _builder.EmitOpCode(ILOpCode.Pop)
                End If
            Else
                ' open appropriate exception handler scope. (Catch or Filter)
                ' if it is a Filter, emit prologue that checks if the type on the stack
                ' converts to what we want.
                If catchBlock.ExceptionFilterOpt Is Nothing Then
                    _builder.OpenLocalScope(ScopeType.Catch, exceptionType)
 
                    If catchBlock.IsSynthesizedAsyncCatchAll Then
                        Debug.Assert(_asyncCatchHandlerOffset < 0)
                        _asyncCatchHandlerOffset = _builder.AllocateILMarker()
                    End If
                Else
                    _builder.OpenLocalScope(ScopeType.Filter)
 
                    ' Filtering starts with simulating regular Catch through an imperative type check
                    ' If this is not our type, then we are done
                    Dim typeCheckPassedLabel As New Object
                    typeCheckFailedLabel = New Object
 
                    _builder.EmitOpCode(ILOpCode.Isinst)
                    _builder.EmitToken(exceptionType, catchBlock.Syntax, _diagnostics)
                    _builder.EmitOpCode(ILOpCode.Dup)
                    _builder.EmitBranch(ILOpCode.Brtrue, typeCheckPassedLabel)
                    _builder.EmitOpCode(ILOpCode.Pop)
                    _builder.EmitIntConstant(0)
                    _builder.EmitBranch(ILOpCode.Br, typeCheckFailedLabel)
 
                    _builder.MarkLabel(typeCheckPassedLabel)
                End If
 
                ' define local if we have one
                Dim localOpt = catchBlock.LocalOpt
                If localOpt IsNot Nothing Then
                    ' TODO: this local can be released when we can release named locals.
                    Dim declNodes = localOpt.DeclaringSyntaxReferences
                    DefineLocal(localOpt, If(Not declNodes.IsEmpty, DirectCast(declNodes(0).GetSyntax(), VisualBasicSyntaxNode), catchBlock.Syntax))
                End If
 
                ' assign the exception variable if we have one
                If exceptionSource IsNot Nothing Then
                    If ShouldNoteProjectErrors() Then
                        _builder.EmitOpCode(ILOpCode.Dup)
                        EmitSetProjectError(catchBlock.Syntax, catchBlock.ErrorLineNumberOpt)
                    End If
 
                    ' here we have our exception on the stack in a form of a reference type (O)
                    ' it means that we have to "unbox" it before storing to the local 
                    ' if exception's type is a generic type parameter.
                    If exceptionSource.Type.IsTypeParameter Then
                        _builder.EmitOpCode(ILOpCode.Unbox_any)
                        EmitSymbolToken(exceptionSource.Type, exceptionSource.Syntax)
                    End If
 
                    ' TODO: parts of the following code is common with AssignmentExpression
                    '       the only major difference is that assignee is on the stack
                    '       consider factoring out common code.
 
                    While exceptionSource.Kind = BoundKind.Sequence
                        Dim seq = DirectCast(exceptionSource, BoundSequence)
                        EmitSideEffects(seq.SideEffects)
                        If seq.ValueOpt Is Nothing Then
                            Exit While
                        Else
                            exceptionSource = seq.ValueOpt
                        End If
                    End While
 
                    Select Case exceptionSource.Kind
                        Case BoundKind.Local
                            Debug.Assert(Not DirectCast(exceptionSource, BoundLocal).LocalSymbol.IsByRef)
                            _builder.EmitLocalStore(GetLocal(DirectCast(exceptionSource, BoundLocal)))
 
                        Case BoundKind.Parameter
                            Dim left = DirectCast(exceptionSource, BoundParameter)
                            ' When assigning to a byref param 
                            ' we need to push param address below the exception
                            If left.ParameterSymbol.IsByRef Then
                                Dim temp = AllocateTemp(exceptionSource.Type, exceptionSource.Syntax)
                                _builder.EmitLocalStore(temp)
                                _builder.EmitLoadArgumentOpcode(ParameterSlot(left))
                                _builder.EmitLocalLoad(temp)
                                FreeTemp(temp)
                            End If
 
                            EmitParameterStore(left)
 
                        Case BoundKind.FieldAccess
                            Dim left = DirectCast(exceptionSource, BoundFieldAccess)
                            If Not left.FieldSymbol.IsShared Then
 
                                Dim stateMachineField = TryCast(left.FieldSymbol, StateMachineFieldSymbol)
                                If (stateMachineField IsNot Nothing) AndAlso (stateMachineField.SlotIndex >= 0) Then
                                    DefineUserDefinedStateMachineHoistedLocal(stateMachineField)
                                End If
 
                                ' When assigning to a field
                                ' we need to push param address below the exception
                                Dim temp = AllocateTemp(exceptionSource.Type, exceptionSource.Syntax)
                                _builder.EmitLocalStore(temp)
 
                                Dim receiver = left.ReceiverOpt
 
                                ' EmitFieldReceiver will handle receivers with type parameter type,
                                ' but we do not know of a test case that will get here with receiver
                                ' of type T. The assert is here to catch such a case. If the assert
                                ' fails, remove the assert and add a corresponding test case.
                                Debug.Assert(receiver.Type.TypeKind <> TypeKind.TypeParameter)
 
                                Dim temp1 = EmitReceiverRef(receiver, isAccessConstrained:=False, addressKind:=AddressKind.[ReadOnly])
                                Debug.Assert(temp1 Is Nothing, "temp is unexpected when assigning to a field")
 
                                _builder.EmitLocalLoad(temp)
                            End If
 
                            EmitFieldStore(left)
 
                        Case Else
                            Throw ExceptionUtilities.UnexpectedValue(exceptionSource.Kind)
                    End Select
                Else
                    If ShouldNoteProjectErrors() Then
                        EmitSetProjectError(catchBlock.Syntax, catchBlock.ErrorLineNumberOpt)
                    Else
                        _builder.EmitOpCode(ILOpCode.Pop)
                    End If
                End If
 
                ' emit the actual filter expression, if we have one, 
                ' and normalize results
                If catchBlock.ExceptionFilterOpt IsNot Nothing Then
                    EmitCondExpr(catchBlock.ExceptionFilterOpt, True)
                    ' Normalize the return value because values other than 0 or 1 produce unspecified results.
                    _builder.EmitIntConstant(0)
                    _builder.EmitOpCode(ILOpCode.Cgt_un)
                    _builder.MarkLabel(typeCheckFailedLabel)
 
                    ' Now we are starting the actual handler
                    _builder.MarkFilterConditionEnd()
 
                    'pop the exception, it should have been already stored to the variable by the filter.
                    _builder.EmitOpCode(ILOpCode.Pop)
                End If
            End If
 
            ' emit actual handler body
            ' Note that it is a block so it will introduce its own scope for locals
            ' it should also have access to the exception local if we have declared one
            ' as that is scoped to the whole Catch/Filter
            EmitBlock(catchBlock.Body)
 
            ' if the end of handler is reachable we should clear project errors. 
            ' (if unreachable, this will not be emitted)
            If ShouldNoteProjectErrors() AndAlso
               (catchBlock.ExceptionFilterOpt Is Nothing OrElse catchBlock.ExceptionFilterOpt.Kind <> BoundKind.UnstructuredExceptionHandlingCatchFilter) Then
                EmitClearProjectError(catchBlock.Syntax)
            End If
 
            _builder.CloseLocalScope()
 
            _currentCatchBlock = oldCatchBlock
        End Sub
 
        ''' <summary>
        ''' Tells if we should emit [Set/Clear]ProjectErrors when entering/leaving handlers
        ''' </summary>
        Private Function ShouldNoteProjectErrors() As Boolean
            Return Not Me._module.SourceModule.ContainingSourceAssembly.IsVbRuntime
        End Function
 
        Private Sub EmitSetProjectError(syntaxNode As SyntaxNode, errorLineNumberOpt As BoundExpression)
            Dim setProjectErrorMethod As MethodSymbol
 
            If errorLineNumberOpt Is Nothing Then
                setProjectErrorMethod = DirectCast(Me._module.Compilation.GetWellKnownTypeMember(WellKnownMember.Microsoft_VisualBasic_CompilerServices_ProjectData__SetProjectError), MethodSymbol)
                ' consumes exception object from the stack
                _builder.EmitOpCode(ILOpCode.Call, -1)
            Else
                setProjectErrorMethod = DirectCast(Me._module.Compilation.GetWellKnownTypeMember(WellKnownMember.Microsoft_VisualBasic_CompilerServices_ProjectData__SetProjectError_Int32), MethodSymbol)
                EmitExpression(errorLineNumberOpt, used:=True)
                ' consumes exception object from the stack and the error line number
                _builder.EmitOpCode(ILOpCode.Call, -2)
            End If
 
            Me.EmitSymbolToken(setProjectErrorMethod, syntaxNode)
        End Sub
 
        Private Sub EmitClearProjectError(syntaxNode As SyntaxNode)
            Const clearProjectError As WellKnownMember = WellKnownMember.Microsoft_VisualBasic_CompilerServices_ProjectData__ClearProjectError
            Dim clearProjectErrorMethod = DirectCast(Me._module.Compilation.GetWellKnownTypeMember(clearProjectError), MethodSymbol)
 
            ' void static with no arguments
            _builder.EmitOpCode(ILOpCode.Call, 0)
            Me.EmitSymbolToken(clearProjectErrorMethod, syntaxNode)
        End Sub
 
        ' specifies whether emitted conditional expression was a constant true/false or not a constant
        Private Enum ConstResKind
            ConstFalse
            ConstTrue
            NotAConst
        End Enum
 
        Private Sub EmitConditionalGoto(boundConditionalGoto As BoundConditionalGoto)
            Dim label As Object = boundConditionalGoto.Label
            Debug.Assert(label IsNot Nothing)
            EmitCondBranch(boundConditionalGoto.Condition, label, boundConditionalGoto.JumpIfTrue)
        End Sub
 
        ' 3.17 The brfalse instruction transfers control to target if value (of type int32, int64, object reference, managed
        'pointer, unmanaged pointer or native int) is zero (false). If value is non-zero (true), execution continues at
        'the next instruction.
        Private Function CanPassToBrfalse(ts As TypeSymbol) As Boolean
            If ts.IsEnumType Then
                ' valid enums are all primitives
                Return True
            End If
 
            Dim tc = ts.PrimitiveTypeCode
            Select Case tc
                Case Cci.PrimitiveTypeCode.Float32, Cci.PrimitiveTypeCode.Float64
                    Return False
 
                Case Cci.PrimitiveTypeCode.NotPrimitive
                    ' if this is a generic type param, verifier will want us to box
                    ' EmitCondBranch knows that
                    Return ts.IsReferenceType
 
                Case Else
                    Debug.Assert(tc <> Cci.PrimitiveTypeCode.Invalid)
                    Debug.Assert(tc <> Cci.PrimitiveTypeCode.Void)
                    Return True
            End Select
        End Function
 
        Private Function TryReduce(condition As BoundBinaryOperator, ByRef sense As Boolean) As BoundExpression
            Dim opKind = condition.OperatorKind And BinaryOperatorKind.OpMask
 
            Debug.Assert(opKind = BinaryOperatorKind.Equals OrElse
                         opKind = BinaryOperatorKind.NotEquals OrElse
                         opKind = BinaryOperatorKind.Is OrElse
                         opKind = BinaryOperatorKind.IsNot)
 
            Dim nonConstOp As BoundExpression
            Dim constOp As ConstantValue = condition.Left.ConstantValueOpt
 
            If constOp IsNot Nothing Then
                nonConstOp = condition.Right
            Else
                constOp = condition.Right.ConstantValueOpt
 
                If constOp Is Nothing Then
                    Return Nothing
                End If
 
                nonConstOp = condition.Left
            End If
 
            Dim nonConstType = nonConstOp.Type
 
            Debug.Assert(nonConstType IsNot Nothing OrElse (nonConstOp.IsNothingLiteral() AndAlso (opKind = BinaryOperatorKind.Is OrElse opKind = BinaryOperatorKind.IsNot)))
 
            If nonConstType IsNot Nothing AndAlso Not CanPassToBrfalse(nonConstType) Then
                Return Nothing
            End If
 
            Dim isBool As Boolean = nonConstType IsNot Nothing AndAlso nonConstType.PrimitiveTypeCode = Microsoft.Cci.PrimitiveTypeCode.Boolean
            Dim isZero As Boolean = constOp.IsDefaultValue
 
            If Not isBool AndAlso Not isZero Then
                Return Nothing
            End If
 
            If isZero Then
                sense = Not sense
            End If
 
            If opKind = BinaryOperatorKind.NotEquals OrElse opKind = BinaryOperatorKind.IsNot Then
                sense = Not sense
            End If
 
            Return nonConstOp
        End Function
 
        Private Const s_IL_OP_CODE_ROW_LENGTH = 4
 
        '    //  <            <=               >                >=
        '    ILOpCode.Blt,    ILOpCode.Ble,    ILOpCode.Bgt,    ILOpCode.Bge,     // Signed
        '    ILOpCode.Bge,    ILOpCode.Bgt,    ILOpCode.Ble,    ILOpCode.Blt,     // Signed Invert
        '    ILOpCode.Blt_un, ILOpCode.Ble_un, ILOpCode.Bgt_un, ILOpCode.Bge_un,  // Unsigned
        '    ILOpCode.Bge_un, ILOpCode.Bgt_un, ILOpCode.Ble_un, ILOpCode.Blt_un,  // Unsigned Invert
        '    ILOpCode.Blt,    ILOpCode.Ble,    ILOpCode.Bgt,    ILOpCode.Bge,     // Float
        '    ILOpCode.Bge_un, ILOpCode.Bgt_un, ILOpCode.Ble_un, ILOpCode.Blt_un,  // Float Invert
 
        Private Shared ReadOnly s_condJumpOpCodes As ILOpCode() = New ILOpCode() {
            ILOpCode.Blt, ILOpCode.Ble, ILOpCode.Bgt, ILOpCode.Bge,
            ILOpCode.Bge, ILOpCode.Bgt, ILOpCode.Ble, ILOpCode.Blt,
            ILOpCode.Blt_un, ILOpCode.Ble_un, ILOpCode.Bgt_un, ILOpCode.Bge_un,
            ILOpCode.Bge_un, ILOpCode.Bgt_un, ILOpCode.Ble_un, ILOpCode.Blt_un,
            ILOpCode.Blt, ILOpCode.Ble, ILOpCode.Bgt, ILOpCode.Bge,
            ILOpCode.Bge_un, ILOpCode.Bgt_un, ILOpCode.Ble_un, ILOpCode.Blt_un}
 
        Private Function CodeForJump(expression As BoundBinaryOperator, sense As Boolean, <Out()> ByRef revOpCode As ILOpCode) As ILOpCode
            Dim opIdx As Integer
            Dim opKind = (expression.OperatorKind And BinaryOperatorKind.OpMask)
            Dim operandType = expression.Left.Type
 
            Debug.Assert(operandType IsNot Nothing OrElse (expression.Left.IsNothingLiteral() AndAlso (opKind = BinaryOperatorKind.Is OrElse opKind = BinaryOperatorKind.IsNot)))
 
            If operandType IsNot Nothing AndAlso operandType.IsBooleanType() Then
                ' Since VB True is -1 but is stored as 1 in IL, relational operations on Boolean must
                ' be reversed to yield the correct results. Note that = and <> do not need reversal.
                Select Case opKind
                    Case BinaryOperatorKind.LessThan
                        opKind = BinaryOperatorKind.GreaterThan
                    Case BinaryOperatorKind.LessThanOrEqual
                        opKind = BinaryOperatorKind.GreaterThanOrEqual
                    Case BinaryOperatorKind.GreaterThan
                        opKind = BinaryOperatorKind.LessThan
                    Case BinaryOperatorKind.GreaterThanOrEqual
                        opKind = BinaryOperatorKind.LessThanOrEqual
                End Select
            End If
 
            Select Case opKind
                Case BinaryOperatorKind.IsNot
                    ValidateReferenceEqualityOperands(expression)
                    Return If(sense, ILOpCode.Bne_un, ILOpCode.Beq)
 
                Case BinaryOperatorKind.Is
                    ValidateReferenceEqualityOperands(expression)
                    Return If(sense, ILOpCode.Beq, ILOpCode.Bne_un)
 
                Case BinaryOperatorKind.Equals
                    Return If(sense, ILOpCode.Beq, ILOpCode.Bne_un)
 
                Case BinaryOperatorKind.NotEquals
                    Return If(sense, ILOpCode.Bne_un, ILOpCode.Beq)
 
                Case BinaryOperatorKind.LessThan
                    opIdx = 0
 
                Case BinaryOperatorKind.LessThanOrEqual
                    opIdx = 1
 
                Case BinaryOperatorKind.GreaterThan
                    opIdx = 2
 
                Case BinaryOperatorKind.GreaterThanOrEqual
                    opIdx = 3
 
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(opKind)
            End Select
 
            If operandType IsNot Nothing Then
                If operandType.IsUnsignedIntegralType() Then
                    opIdx += 2 * s_IL_OP_CODE_ROW_LENGTH 'unsigned
                Else
                    If operandType.IsFloatingType() Then
                        opIdx += 4 * s_IL_OP_CODE_ROW_LENGTH 'float
                    End If
                End If
            End If
 
            Dim revOpIdx = opIdx
 
            If Not sense Then
                opIdx += s_IL_OP_CODE_ROW_LENGTH 'invert op
            Else
                revOpIdx += s_IL_OP_CODE_ROW_LENGTH 'invert orev
            End If
 
            revOpCode = s_condJumpOpCodes(revOpIdx)
            Return s_condJumpOpCodes(opIdx)
        End Function
 
        ' generate a jump to dest if (condition == sense) is true
        ' it is ok if lazyDest is Nothing
        ' if lazyDest is needed it will be initialized to a new object
        Private Sub EmitCondBranch(condition As BoundExpression, ByRef lazyDest As Object, sense As Boolean)
            _recursionDepth += 1
 
            If _recursionDepth > 1 Then
                StackGuard.EnsureSufficientExecutionStack(_recursionDepth)
 
                EmitCondBranchCore(condition, lazyDest, sense)
            Else
                EmitCondBranchCoreWithStackGuard(condition, lazyDest, sense)
            End If
 
            _recursionDepth -= 1
        End Sub
 
        Private Sub EmitCondBranchCoreWithStackGuard(condition As BoundExpression, ByRef lazyDest As Object, sense As Boolean)
            Debug.Assert(_recursionDepth = 1)
 
            Try
                EmitCondBranchCore(condition, lazyDest, sense)
                Debug.Assert(_recursionDepth = 1)
 
            Catch ex As InsufficientExecutionStackException
                _diagnostics.Add(ERRID.ERR_TooLongOrComplexExpression,
                                 BoundTreeVisitor.CancelledByStackGuardException.GetTooLongOrComplexExpressionErrorLocation(condition))
                Throw New EmitCancelledException()
            End Try
        End Sub
 
        Private Sub EmitCondBranchCore(condition As BoundExpression, ByRef lazyDest As Object, sense As Boolean)
oneMoreTime:
            Dim ilcode As ILOpCode
            Dim constExprValue = condition.ConstantValueOpt
 
            If constExprValue IsNot Nothing Then
                ' make sure that only the bool bits are set or it is a Nothing literal 
                ' or it is a string literal in which case it is equal to True
                Debug.Assert(constExprValue.Discriminator = ConstantValueTypeDiscriminator.Boolean OrElse
                             constExprValue.Discriminator = ConstantValueTypeDiscriminator.String OrElse
                             constExprValue.IsNothing)
 
                Dim taken As Boolean = constExprValue.IsDefaultValue <> sense
                If taken Then
                    lazyDest = If(lazyDest, New Object())
                    _builder.EmitBranch(ILOpCode.Br, lazyDest)
                Else
                    ' otherwise this branch will never be taken, so just fall through...
                End If
 
                Return
            End If
 
            Select Case condition.Kind
                Case BoundKind.BinaryOperator
                    Dim binOp = DirectCast(condition, BoundBinaryOperator)
                    Dim testBothArgs As Boolean = sense
 
                    Select Case binOp.OperatorKind And BinaryOperatorKind.OpMask
                        Case BinaryOperatorKind.OrElse
                            testBothArgs = Not testBothArgs
                            GoTo BinaryOperatorKindLogicalAnd
 
                        Case BinaryOperatorKind.AndAlso
BinaryOperatorKindLogicalAnd:
                            If testBothArgs Then
                                ' gotoif(a != sense) fallThrough
                                ' gotoif(b == sense) dest
                                ' fallThrough:
                                Dim lazyFallThrough = New Object
                                EmitCondBranch(binOp.Left, lazyFallThrough, Not sense)
                                EmitCondBranch(binOp.Right, lazyDest, sense)
 
                                If (lazyFallThrough IsNot Nothing) Then
                                    _builder.MarkLabel(lazyFallThrough)
                                End If
                            Else
                                EmitCondBranch(binOp.Left, lazyDest, sense)
                                condition = binOp.Right
                                GoTo oneMoreTime
                            End If
                            Return
 
                        Case BinaryOperatorKind.IsNot,
                             BinaryOperatorKind.Is
 
                            ValidateReferenceEqualityOperands(binOp)
                            GoTo BinaryOperatorKindEqualsNotEquals
 
                        Case BinaryOperatorKind.Equals,
                             BinaryOperatorKind.NotEquals
BinaryOperatorKindEqualsNotEquals:
                            Dim reduced = TryReduce(binOp, sense)
                            If reduced IsNot Nothing Then
                                condition = reduced
                                GoTo oneMoreTime
                            End If
                            GoTo BinaryOperatorKindLessThan
 
                        Case BinaryOperatorKind.LessThan,
                             BinaryOperatorKind.LessThanOrEqual,
                             BinaryOperatorKind.GreaterThan,
                             BinaryOperatorKind.GreaterThanOrEqual
 
BinaryOperatorKindLessThan:
                            EmitExpression(binOp.Left, True)
                            EmitExpression(binOp.Right, True)
                            Dim revOpCode As ILOpCode
                            ilcode = CodeForJump(binOp, sense, revOpCode)
                            lazyDest = If(lazyDest, New Object())
                            _builder.EmitBranch(ilcode, lazyDest, revOpCode)
                            Return
                    End Select
 
                    ' none of above. 
                    ' then it is regular binary expression - Or, And, Xor ...
                    GoTo OtherExpressions
 
                Case BoundKind.UnaryOperator
                    Dim unOp = DirectCast(condition, BoundUnaryOperator)
                    If (unOp.OperatorKind = UnaryOperatorKind.Not) Then
                        Debug.Assert(unOp.Type.IsBooleanType())
                        sense = Not sense
                        condition = unOp.Operand
                        GoTo oneMoreTime
                    Else
                        GoTo OtherExpressions
                    End If
 
                Case BoundKind.TypeOf
 
                    Dim typeOfExpression = DirectCast(condition, BoundTypeOf)
 
                    EmitTypeOfExpression(typeOfExpression, used:=True, optimize:=True)
 
                    If typeOfExpression.IsTypeOfIsNotExpression Then
                        sense = Not sense
                    End If
 
                    ilcode = If(sense, ILOpCode.Brtrue, ILOpCode.Brfalse)
                    lazyDest = If(lazyDest, New Object())
                    _builder.EmitBranch(ilcode, lazyDest)
                    Return
 
#If False Then
                Case BoundKind.AsOperator
                    Dim asOp = DirectCast(condition, BoundIsOperator)
                    EmitExpression(asOp.Operand, True)
                    _builder.EmitOpCode(ILOpCode.Isinst)
                    EmitSymbolToken(asOp.TargetType.Type)
                    ilcode = If(sense, ILOpCode.Brtrue, ILOpCode.Brfalse)
                    _builder.EmitBranch(ilcode, dest)
                    Return
#End If
                Case BoundKind.Sequence
                    Dim sequence = DirectCast(condition, BoundSequence)
                    EmitSequenceCondBranch(sequence, lazyDest, sense)
                    Return
 
                Case Else
OtherExpressions:
                    EmitExpression(condition, True)
 
                    Dim conditionType = condition.Type
                    If Not conditionType.IsValueType AndAlso Not IsVerifierReference(conditionType) Then
                        EmitBox(conditionType, condition.Syntax)
                    End If
 
                    ilcode = If(sense, ILOpCode.Brtrue, ILOpCode.Brfalse)
                    lazyDest = If(lazyDest, New Object())
                    _builder.EmitBranch(ilcode, lazyDest)
                    Return
            End Select
        End Sub
 
        <Conditional("DEBUG")>
        Private Sub ValidateReferenceEqualityOperands(binOp As BoundBinaryOperator)
            Debug.Assert(binOp.Left.IsNothingLiteral() OrElse binOp.Left.Type.SpecialType = SpecialType.System_Object OrElse binOp.WasCompilerGenerated)
            Debug.Assert(binOp.Right.IsNothingLiteral() OrElse binOp.Right.Type.SpecialType = SpecialType.System_Object OrElse binOp.WasCompilerGenerated)
        End Sub
 
        'TODO: is this to fold value? Same in C#?
        Private Sub EmitSequenceCondBranch(sequence As BoundSequence, ByRef lazyDest As Object, sense As Boolean)
            Dim hasLocals As Boolean = Not sequence.Locals.IsEmpty
            If hasLocals Then
                _builder.OpenLocalScope()
 
                For Each local In sequence.Locals
                    Me.DefineLocal(local, sequence.Syntax)
                Next
            End If
 
            Me.EmitSideEffects(sequence.SideEffects)
            Debug.Assert(sequence.ValueOpt IsNot Nothing)
            Me.EmitCondBranch(sequence.ValueOpt, lazyDest, sense)
 
            If hasLocals Then
                _builder.CloseLocalScope()
 
                For Each local In sequence.Locals
                    Me.FreeLocal(local)
                Next
            End If
        End Sub
 
        Private Sub EmitLabelStatement(boundLabelStatement As BoundLabelStatement)
            _builder.MarkLabel(boundLabelStatement.Label)
        End Sub
 
        Private Sub EmitGotoStatement(boundGotoStatement As BoundGotoStatement)
            ' if branch leaves current Catch block we need to emit ClearProjectError()
            If ShouldNoteProjectErrors() Then
                If _currentCatchBlock IsNot Nothing AndAlso
                    (_currentCatchBlock.ExceptionFilterOpt Is Nothing OrElse _currentCatchBlock.ExceptionFilterOpt.Kind <> BoundKind.UnstructuredExceptionHandlingCatchFilter) AndAlso
                    Not LabelFinder.NodeContainsLabel(_currentCatchBlock, boundGotoStatement.Label) Then
 
                    EmitClearProjectError(boundGotoStatement.Syntax)
 
                End If
            End If
 
            _builder.EmitBranch(ILOpCode.Br, boundGotoStatement.Label)
        End Sub
 
        ''' <summary>
        ''' tells if given node contains a label statement that defines given label symbol
        ''' </summary>
        Private Class LabelFinder
            Inherits StatementWalker
 
            Private ReadOnly _label As LabelSymbol
            Private _found As Boolean = False
 
            Private Sub New(label As LabelSymbol)
                Me._label = label
            End Sub
 
            Public Overrides Function Visit(node As BoundNode) As BoundNode
                If Not _found AndAlso TypeOf node IsNot BoundExpression Then
                    Return MyBase.Visit(node)
                End If
 
                Return Nothing
            End Function
 
            Public Overrides Function VisitLabelStatement(node As BoundLabelStatement) As BoundNode
                If node.Label Is Me._label Then
                    _found = True
                End If
 
                Return MyBase.VisitLabelStatement(node)
            End Function
 
            Public Shared Function NodeContainsLabel(node As BoundNode, label As LabelSymbol) As Boolean
                Dim finder = New LabelFinder(label)
                finder.Visit(node)
 
                Return finder._found
            End Function
        End Class
 
        Private Sub EmitReturnStatement(boundReturnStatement As BoundReturnStatement)
            Me.EmitExpression(boundReturnStatement.ExpressionOpt, True)
            _builder.EmitRet(boundReturnStatement.ExpressionOpt Is Nothing)
        End Sub
 
        Private Sub EmitThrowStatement(boundThrowStatement As BoundThrowStatement)
            Dim operand = boundThrowStatement.ExpressionOpt
 
            If operand IsNot Nothing Then
                EmitExpression(operand, used:=True)
 
                Dim operandType = operand.Type
                ' "Throw Nothing" is not supported by the language
                ' so operand.Type should always be set.
                Debug.Assert(operandType IsNot Nothing)
                If (operandType IsNot Nothing) AndAlso (operandType.TypeKind = TypeKind.TypeParameter) Then
                    EmitBox(operandType, operand.Syntax)
                End If
            End If
 
            _builder.EmitThrow(operand Is Nothing)
        End Sub
 
        Private Sub EmitSelectStatement(boundSelectStatement As BoundSelectStatement)
            Debug.Assert(boundSelectStatement.RecommendSwitchTable)
 
            Dim selectExpression = boundSelectStatement.ExpressionStatement.Expression
            Dim caseBlocks = boundSelectStatement.CaseBlocks
            Dim exitLabel = boundSelectStatement.ExitLabel
            Dim fallThroughLabel = exitLabel
 
            Debug.Assert(selectExpression.Type IsNot Nothing)
            Debug.Assert(caseBlocks.Any())
 
            ' Create labels for case blocks
            Dim caseBlockLabels As ImmutableArray(Of GeneratedLabelSymbol) = CreateCaseBlockLabels(caseBlocks)
 
            ' Create an array of key value pairs (key: case clause constant value, value: case block label)
            ' for emitting switch table based header.
            ' This function also ensures the correct fallThroughLabel is set, i.e. case else block label if one exists, otherwise exit label.
            Dim caseLabelsForEmit As KeyValuePair(Of ConstantValue, Object)() = GetCaseLabelsForEmitSwitchHeader(caseBlocks, caseBlockLabels, fallThroughLabel)
 
            ' Emit switch table header
            EmitSwitchTableHeader(selectExpression, caseLabelsForEmit, fallThroughLabel)
 
            ' Emit case blocks
            EmitCaseBlocks(caseBlocks, caseBlockLabels, exitLabel)
 
            ' Emit exit label
            _builder.MarkLabel(exitLabel)
        End Sub
 
        ' Create a label for each case block
        Private Function CreateCaseBlockLabels(caseBlocks As ImmutableArray(Of BoundCaseBlock)) As ImmutableArray(Of GeneratedLabelSymbol)
            Debug.Assert(Not caseBlocks.IsEmpty)
 
            Dim caseBlockLabels = ArrayBuilder(Of GeneratedLabelSymbol).GetInstance(caseBlocks.Length)
            Dim cur As Integer = 0
 
            For Each caseBlock In caseBlocks
                cur = cur + 1
                caseBlockLabels.Add(New GeneratedLabelSymbol("Case Block " + cur.ToString()))
            Next
 
            Return caseBlockLabels.ToImmutableAndFree()
        End Function
 
        ' Creates an array of key value pairs (key: case clause constant value, value: case block label)
        ' for emitting switch table based header.
        ' This function also ensures the correct fallThroughLabel is set, i.e. case else block label if one exists, otherwise exit label.
        Private Function GetCaseLabelsForEmitSwitchHeader(
            caseBlocks As ImmutableArray(Of BoundCaseBlock),
            caseBlockLabels As ImmutableArray(Of GeneratedLabelSymbol),
            ByRef fallThroughLabel As LabelSymbol
        ) As KeyValuePair(Of ConstantValue, Object)()
 
            Debug.Assert(Not caseBlocks.IsEmpty)
            Debug.Assert(Not caseBlockLabels.IsEmpty)
            Debug.Assert(caseBlocks.Length = caseBlockLabels.Length)
 
            Dim labelsBuilder = ArrayBuilder(Of KeyValuePair(Of ConstantValue, Object)).GetInstance()
            Dim constantsSet = New HashSet(Of ConstantValue)(New SwitchConstantValueHelper.SwitchLabelsComparer())
 
            Dim cur As Integer = 0
 
            For Each caseBlock In caseBlocks
                Dim caseBlockLabel = caseBlockLabels(cur)
                Dim caseClauses = caseBlock.CaseStatement.CaseClauses
 
                If caseClauses.Any() Then
                    For Each caseClause In caseClauses
                        Dim constant As ConstantValue
 
                        Select Case caseClause.Kind
                            Case BoundKind.SimpleCaseClause
                                Dim simpleCaseClause = DirectCast(caseClause, BoundSimpleCaseClause)
 
                                Debug.Assert(simpleCaseClause.ValueOpt IsNot Nothing)
                                Debug.Assert(simpleCaseClause.ConditionOpt Is Nothing)
 
                                constant = simpleCaseClause.ValueOpt.ConstantValueOpt
 
                            Case BoundKind.RelationalCaseClause
                                Dim relationalCaseClause = DirectCast(caseClause, BoundRelationalCaseClause)
 
                                Debug.Assert(relationalCaseClause.OperatorKind = BinaryOperatorKind.Equals)
                                Debug.Assert(relationalCaseClause.ValueOpt IsNot Nothing)
                                Debug.Assert(relationalCaseClause.ConditionOpt Is Nothing)
 
                                constant = relationalCaseClause.ValueOpt.ConstantValueOpt
 
                            Case BoundKind.RangeCaseClause
                                ' TODO: For now we use IF lists if we encounter
                                ' TODO: BoundRangeCaseClause, we should not reach here.
                                Throw ExceptionUtilities.UnexpectedValue(caseClause.Kind)
 
                            Case Else
                                Throw ExceptionUtilities.UnexpectedValue(caseClause.Kind)
                        End Select
 
                        Debug.Assert(constant IsNot Nothing)
                        Debug.Assert(SwitchConstantValueHelper.IsValidSwitchCaseLabelConstant(constant))
 
                        ' If we have duplicate case value constants, use the lexically first case constant.
                        If Not constantsSet.Contains(constant) Then
                            labelsBuilder.Add(New KeyValuePair(Of ConstantValue, Object)(constant, caseBlockLabel))
                            constantsSet.Add(constant)
                        End If
                    Next
                Else
                    Debug.Assert(caseBlock.Syntax.Kind = SyntaxKind.CaseElseBlock)
 
                    ' We have a case else block, update the fallThroughLabel to the corresponding caseBlockLabel
                    fallThroughLabel = caseBlockLabel
                End If
 
                cur = cur + 1
            Next
 
            Return labelsBuilder.ToArrayAndFree()
        End Function
 
        Private Sub EmitSwitchTableHeader(selectExpression As BoundExpression, caseLabels As KeyValuePair(Of ConstantValue, Object)(), fallThroughLabel As LabelSymbol)
            Debug.Assert(selectExpression.Type IsNot Nothing)
            Debug.Assert(caseLabels IsNot Nothing)
 
            If Not caseLabels.Any() Then
                ' No case labels, emit branch to fallThroughLabel
                _builder.EmitBranch(ILOpCode.Br, fallThroughLabel)
            Else
 
                Dim exprType = selectExpression.Type
                Dim temp As LocalDefinition = Nothing
 
                If exprType.SpecialType <> SpecialType.System_String Then
                    If selectExpression.Kind = BoundKind.Local AndAlso Not DirectCast(selectExpression, BoundLocal).LocalSymbol.IsByRef Then
                        _builder.EmitIntegerSwitchJumpTable(caseLabels, fallThroughLabel, GetLocal(DirectCast(selectExpression, BoundLocal)), keyTypeCode:=exprType.GetEnumUnderlyingTypeOrSelf.PrimitiveTypeCode)
 
                    ElseIf selectExpression.Kind = BoundKind.Parameter AndAlso Not DirectCast(selectExpression, BoundParameter).ParameterSymbol.IsByRef Then
                        _builder.EmitIntegerSwitchJumpTable(caseLabels, fallThroughLabel, ParameterSlot(DirectCast(selectExpression, BoundParameter)), keyTypeCode:=exprType.GetEnumUnderlyingTypeOrSelf.PrimitiveTypeCode)
 
                    Else
                        EmitExpression(selectExpression, True)
                        temp = AllocateTemp(exprType, selectExpression.Syntax)
                        _builder.EmitLocalStore(temp)
 
                        _builder.EmitIntegerSwitchJumpTable(caseLabels, fallThroughLabel, temp, keyTypeCode:=exprType.GetEnumUnderlyingTypeOrSelf.PrimitiveTypeCode)
                    End If
                Else
                    If selectExpression.Kind = BoundKind.Local AndAlso Not DirectCast(selectExpression, BoundLocal).LocalSymbol.IsByRef Then
                        EmitStringSwitchJumpTable(caseLabels, fallThroughLabel, GetLocal(DirectCast(selectExpression, BoundLocal)), selectExpression.Syntax)
 
                    Else
                        EmitExpression(selectExpression, True)
                        temp = AllocateTemp(exprType, selectExpression.Syntax)
                        _builder.EmitLocalStore(temp)
 
                        EmitStringSwitchJumpTable(caseLabels, fallThroughLabel, temp, selectExpression.Syntax)
                    End If
                End If
 
                If temp IsNot Nothing Then
                    FreeTemp(temp)
                End If
            End If
        End Sub
 
        Private Sub EmitStringSwitchJumpTable(caseLabels As KeyValuePair(Of ConstantValue, Object)(), fallThroughLabel As LabelSymbol, key As LocalDefinition, syntaxNode As SyntaxNode)
            Dim genHashTableSwitch As Boolean = SwitchStringJumpTableEmitter.ShouldGenerateHashTableSwitch(caseLabels.Length)
            Dim keyHash As LocalDefinition = Nothing
 
            If genHashTableSwitch Then
                Dim privateImplClass = _module.GetPrivateImplClass(syntaxNode, _diagnostics)
                Dim stringHashMethodRef As Microsoft.Cci.IReference = privateImplClass.GetMethod(PrivateImplementationDetails.SynthesizedStringHashFunctionName)
                Debug.Assert(stringHashMethodRef IsNot Nothing)
 
                ' static uint ComputeStringHash(string s)
                ' pop 1 (s)
                ' push 1 (uint return value)
                ' stackAdjustment = (pushCount - popCount) = 0
 
                _builder.EmitLocalLoad(key)
                _builder.EmitOpCode(ILOpCode.[Call], stackAdjustment:=0)
                _builder.EmitToken(stringHashMethodRef, syntaxNode, _diagnostics)
 
                Dim UInt32Type = DirectCast(_module.GetSpecialType(SpecialType.System_UInt32, syntaxNode, _diagnostics).GetInternalSymbol(), TypeSymbol)
                keyHash = AllocateTemp(UInt32Type, syntaxNode)
 
                _builder.EmitLocalStore(keyHash)
            End If
 
            ' Prefer embedded version of the member if present
            Dim embeddedOperatorsType As NamedTypeSymbol = Me._module.Compilation.GetWellKnownType(WellKnownType.Microsoft_VisualBasic_CompilerServices_EmbeddedOperators)
            Dim compareStringMember As WellKnownMember =
                If(embeddedOperatorsType.IsErrorType AndAlso TypeOf embeddedOperatorsType Is MissingMetadataTypeSymbol,
                   WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__CompareStringStringStringBoolean,
                   WellKnownMember.Microsoft_VisualBasic_CompilerServices_EmbeddedOperators__CompareStringStringStringBoolean)
 
            Dim stringCompareMethod = DirectCast(Me._module.Compilation.GetWellKnownTypeMember(compareStringMember), MethodSymbol)
            Dim stringCompareMethodRef As Cci.IReference = Me._module.Translate(stringCompareMethod, needDeclaration:=False, syntaxNodeOpt:=syntaxNode, diagnostics:=_diagnostics)
 
            Dim compareDelegate As SwitchStringJumpTableEmitter.EmitStringCompareAndBranch =
                Sub(keyArg, stringConstant, targetLabel)
                    EmitStringCompareAndBranch(keyArg, syntaxNode, stringConstant, targetLabel, stringCompareMethodRef)
                End Sub
 
            _builder.EmitStringSwitchJumpTable(
                            caseLabels,
                            fallThroughLabel,
                            key,
                            keyHash,
                            compareDelegate,
                            AddressOf SynthesizedStringSwitchHashMethod.ComputeStringHash)
 
            If keyHash IsNot Nothing Then
                FreeTemp(keyHash)
            End If
        End Sub
 
        ''' <summary>
        ''' Delegate to emit string compare call and conditional branch based on the compare result.
        ''' </summary>
        ''' <param name="key">Key to compare</param>
        ''' <param name="syntaxNode">Node for diagnostics</param>
        ''' <param name="stringConstant">Case constant to compare the key against</param>
        ''' <param name="targetLabel">Target label to branch to if key = stringConstant</param>
        ''' <param name="stringCompareMethodRef">String equality method</param>
        Private Sub EmitStringCompareAndBranch(key As LocalOrParameter, syntaxNode As SyntaxNode, stringConstant As ConstantValue, targetLabel As Object, stringCompareMethodRef As Microsoft.Cci.IReference)
            ' Emit compare and branch:
 
            ' If key = stringConstant Then
            '      Goto targetLabel
            ' End If
 
            Debug.Assert(stringCompareMethodRef IsNot Nothing)
 
#If DEBUG Then
            Dim assertDiagnostics = DiagnosticBag.GetInstance()
            Debug.Assert(stringCompareMethodRef Is Me._module.Translate(DirectCast(
                                                        If(TypeOf Me._module.Compilation.GetWellKnownType(WellKnownType.Microsoft_VisualBasic_CompilerServices_EmbeddedOperators) Is MissingMetadataTypeSymbol,
                                                           Me._module.Compilation.GetWellKnownTypeMember(WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__CompareStringStringStringBoolean),
                                                           Me._module.Compilation.GetWellKnownTypeMember(WellKnownMember.Microsoft_VisualBasic_CompilerServices_EmbeddedOperators__CompareStringStringStringBoolean)), MethodSymbol), needDeclaration:=False,
                                                           syntaxNodeOpt:=DirectCast(syntaxNode, VisualBasicSyntaxNode), diagnostics:=assertDiagnostics))
            assertDiagnostics.Free()
#End If
 
            ' Public Shared Function CompareString(Left As String, Right As String, TextCompare As Boolean) As Integer
            ' pop 3 (Left, Right, TextCompare)
            ' push 1 (Integer return value)
 
            ' stackAdjustment = (pushCount - popCount) = -2
 
            ' NOTE: We generate string switch table only for Option Compare Binary, i.e. TextCompare = False
 
            _builder.EmitLoad(key)
            _builder.EmitConstantValue(stringConstant)
            _builder.EmitConstantValue(ConstantValue.False)
            _builder.EmitOpCode(ILOpCode.Call, stackAdjustment:=-2)
            _builder.EmitToken(stringCompareMethodRef, syntaxNode, _diagnostics)
 
            ' CompareString returns 0 if Left and Right strings are equal.
            ' Branch to targetLabel if CompareString returned 0.
            _builder.EmitBranch(ILOpCode.Brfalse, targetLabel, ILOpCode.Brtrue)
        End Sub
 
        Private Sub EmitCaseBlocks(caseBlocks As ImmutableArray(Of BoundCaseBlock), caseBlockLabels As ImmutableArray(Of GeneratedLabelSymbol), exitLabel As LabelSymbol)
            Debug.Assert(Not caseBlocks.IsEmpty)
            Debug.Assert(Not caseBlockLabels.IsEmpty)
            Debug.Assert(caseBlocks.Length = caseBlockLabels.Length)
 
            Dim cur As Integer = 0
            For Each caseBlock In caseBlocks
                ' Emit case block label
                _builder.MarkLabel(caseBlockLabels(cur))
                cur = cur + 1
 
                ' Emit case statement sequence point
 
                Dim caseStatement = caseBlock.CaseStatement
                If Not caseStatement.WasCompilerGenerated Then
                    Debug.Assert(caseStatement.Syntax IsNot Nothing)
 
                    If _emitPdbSequencePoints Then
                        EmitSequencePoint(caseStatement.Syntax)
                    End If
 
                    If _ilEmitStyle = ILEmitStyle.Debug Then
                        ' Emit nop for the case statement otherwise the above sequence point
                        ' will get associated with the first statement in subsequent case block.
                        ' This matches the native compiler codegen.
                        _builder.EmitOpCode(ILOpCode.Nop)
                    End If
                End If
 
                ' Emit case block body
                EmitBlock(caseBlock.Body)
 
                ' Emit a branch to exit label
                _builder.EmitBranch(ILOpCode.Br, exitLabel)
            Next
        End Sub
 
        Private Sub EmitBlock(scope As BoundBlock)
            Dim hasLocals As Boolean = Not scope.Locals.IsEmpty
            If hasLocals Then
                _builder.OpenLocalScope()
 
                For Each local In scope.Locals
                    Dim declNodes = local.DeclaringSyntaxReferences
                    Me.DefineLocal(local, If(declNodes.IsEmpty, scope.Syntax, declNodes(0).GetVisualBasicSyntax()))
                Next
            End If
 
            For Each statement In scope.Statements
                EmitStatement(statement)
            Next
 
            If hasLocals Then
                _builder.CloseLocalScope()
                'TODO: can we free any locals here? Perhaps nameless temps?
            End If
        End Sub
 
        Private Function DefineLocal(local As LocalSymbol, syntaxNode As SyntaxNode) As LocalDefinition
            Dim dynamicTransformFlags = ImmutableArray(Of Boolean).Empty
            Dim tupleElementNames = If(Not local.IsCompilerGenerated AndAlso local.Type.ContainsTupleNames(),
                VisualBasicCompilation.TupleNamesEncoder.Encode(local.Type),
                ImmutableArray(Of String).Empty)
 
            ' We're treating constants of type Decimal and DateTime as local here to not create a new instance for each time
            ' the value is accessed. This means there will be one local in the scope for this constant.
            ' This has the side effect that this constant will later on appear in the PDB file as a common local and one is able
            ' to modify the value in the debugger (which is a regression from Dev10).
            ' To fix this while keeping the behavior of having just one local for the const, one would need to preserve the 
            ' information that this local is a ConstantButNotMetadataConstant (update ScopeManager.DeclareLocal & LocalDefinition)
            ' and modify PEWriter.Initialize to DefineLocalConstant instead of DefineLocalVariable if the local is 
            ' ConstantButNotMetadataConstant.
            ' See bug #11047
 
            If local.HasConstantValue Then
                Dim compileTimeValue As MetadataConstant = _module.CreateConstant(local.Type, local.ConstantValue, syntaxNode, _diagnostics)
                Dim localConstantDef = New LocalConstantDefinition(
                    local.Name,
                    If(local.Locations.FirstOrDefault(), Location.None),
                    compileTimeValue,
                    dynamicTransformFlags:=dynamicTransformFlags,
                    tupleElementNames:=tupleElementNames)
                ' Reference in the scope for debugging purpose
                _builder.AddLocalConstantToScope(localConstantDef)
                Return Nothing
            End If
 
            If Me.IsStackLocal(local) Then
                Return Nothing
            End If
 
            Dim translatedType = _module.Translate(local.Type, syntaxNodeOpt:=syntaxNode, diagnostics:=_diagnostics)
            ' Even though we don't need the token immediately, we will need it later when signature for the local is emitted.
            ' Also, requesting the token has side-effect of registering types used, which is critical for embedded types (NoPia, VBCore, etc).
            _module.GetFakeSymbolTokenForIL(translatedType, syntaxNode, _diagnostics)
 
            Dim constraints = If(local.IsByRef, LocalSlotConstraints.ByRef, LocalSlotConstraints.None) Or
                If(local.IsPinned, LocalSlotConstraints.Pinned, LocalSlotConstraints.None)
 
            Dim localId As LocalDebugId = Nothing
            Dim name As String = GetLocalDebugName(local, localId)
 
            Dim synthesizedKind = local.SynthesizedKind
            Dim localDef = _builder.LocalSlotManager.DeclareLocal(
                type:=translatedType,
                symbol:=local,
                name:=name,
                kind:=synthesizedKind,
                id:=localId,
                pdbAttributes:=synthesizedKind.PdbAttributes(),
                constraints:=constraints,
                dynamicTransformFlags:=dynamicTransformFlags,
                tupleElementNames:=tupleElementNames,
                isSlotReusable:=synthesizedKind.IsSlotReusable(_ilEmitStyle <> ILEmitStyle.Release))
 
            ' If named, add it to the local debug scope.
            If localDef.Name IsNot Nothing Then
                _builder.AddLocalToScope(localDef)
            End If
 
            Return localDef
        End Function
 
        ''' <summary>
        ''' Gets the name And id of the local that are going to be generated into the debug metadata.
        ''' </summary>
        Private Function GetLocalDebugName(local As LocalSymbol, <Out> ByRef localId As LocalDebugId) As String
            localId = LocalDebugId.None
 
            If local.IsImportedFromMetadata Then
                Return local.Name
            End If
 
            ' We include function value locals in async and iterator methods so that appropriate
            ' errors can be reported when users attempt to refer to them.  However, there's no
            ' reason to actually emit them into the resulting MoveNext method, because they will
            ' never be accessed.  Unfortunately, for implementation-specific reasons, dropping them
            ' would be non-trivial.  Instead, we drop their names so that they do not appear while
            ' debugging (esp in the Locals window).
            If local.DeclarationKind = LocalDeclarationKind.FunctionValue AndAlso
                TypeOf _method Is SynthesizedStateMachineMethod Then
                Return Nothing
            End If
 
            Dim localKind = local.SynthesizedKind
 
            ' only user-defined locals should be named during lowering:
            Debug.Assert((local.Name Is Nothing) = (localKind <> SynthesizedLocalKind.UserDefined))
 
            If Not localKind.IsLongLived() Then
                Return Nothing
            End If
 
            If _ilEmitStyle = ILEmitStyle.Debug Then
                Dim syntax = local.GetDeclaratorSyntax()
                Dim syntaxOffset = _method.CalculateLocalSyntaxOffset(syntax.SpanStart, syntax.SyntaxTree)
 
                Dim ordinal = _synthesizedLocalOrdinals.AssignLocalOrdinal(localKind, syntaxOffset)
 
                ' user-defined locals should have 0 ordinal
                Debug.Assert(ordinal = 0 OrElse localKind <> SynthesizedLocalKind.UserDefined)
 
                localId = New LocalDebugId(syntaxOffset, ordinal)
            End If
 
            If local.Name IsNot Nothing Then
                Return local.Name
            End If
 
            Return GeneratedNames.MakeSynthesizedLocalName(localKind, _uniqueNameId)
        End Function
 
        Private Function IsSlotReusable(local As LocalSymbol) As Boolean
            Return local.SynthesizedKind.IsSlotReusable(_ilEmitStyle <> ILEmitStyle.Release)
        End Function
 
        Private Sub FreeLocal(local As LocalSymbol)
            'TODO: releasing locals with name NYI.
            'NOTE: VB considers named local's extent to be whole method 
            '      so releasing them may just not be possible.
            If local.Name Is Nothing AndAlso IsSlotReusable(local) AndAlso Not IsStackLocal(local) Then
                _builder.LocalSlotManager.FreeLocal(local)
            End If
        End Sub
 
        ''' <summary>
        ''' Gets already declared and initialized local.
        ''' </summary>
        Private Function GetLocal(localExpression As BoundLocal) As LocalDefinition
            Dim symbol = localExpression.LocalSymbol
            Return GetLocal(symbol)
        End Function
 
        Private Function GetLocal(symbol As LocalSymbol) As LocalDefinition
            Return _builder.LocalSlotManager.GetLocal(symbol)
        End Function
 
        ''' <summary>
        ''' Allocates a temp without identity.
        ''' </summary>
        Private Function AllocateTemp(type As TypeSymbol, syntaxNode As SyntaxNode) As LocalDefinition
            Return _builder.LocalSlotManager.AllocateSlot(
                Me._module.Translate(type, syntaxNodeOpt:=syntaxNode, diagnostics:=_diagnostics),
                LocalSlotConstraints.None)
        End Function
 
        ''' <summary>
        ''' Frees a temp without identity.
        ''' </summary>
        Private Sub FreeTemp(temp As LocalDefinition)
            _builder.LocalSlotManager.FreeSlot(temp)
        End Sub
 
        ''' <summary>
        ''' Frees an optional temp.
        ''' </summary>
        Private Sub FreeOptTemp(temp As LocalDefinition)
            If temp IsNot Nothing Then
                FreeTemp(temp)
            End If
        End Sub
 
        Private Sub EmitUnstructuredExceptionOnErrorSwitch(node As BoundUnstructuredExceptionOnErrorSwitch)
            EmitExpression(node.Value, used:=True)
            EmitSwitch(node.Jumps)
        End Sub
 
        Private Sub EmitSwitch(jumps As ImmutableArray(Of BoundGotoStatement))
            Dim labels(jumps.Length - 1) As Object
 
            For i As Integer = 0 To jumps.Length - 1
                labels(i) = jumps(i).Label
            Next
 
            _builder.EmitSwitch(labels)
        End Sub
 
        Private Sub EmitStateMachineScope(scope As BoundStateMachineScope)
            _builder.OpenLocalScope(ScopeType.StateMachineVariable)
 
            For Each field In scope.Fields
                Dim stateMachineField = DirectCast(field, StateMachineFieldSymbol)
                If stateMachineField.SlotIndex >= 0 Then
                    DefineUserDefinedStateMachineHoistedLocal(stateMachineField)
                End If
            Next
 
            EmitStatement(scope.Statement)
            _builder.CloseLocalScope()
        End Sub
 
        Private Sub DefineUserDefinedStateMachineHoistedLocal(field As StateMachineFieldSymbol)
            Debug.Assert(field.SlotIndex >= 0)
 
            If _module.debugInformationFormat = DebugInformationFormat.Pdb Then
                'Native PDBs: VB EE uses name mangling to match up original locals and the fields where they are hoisted
                'The scoping information is passed by recording PDB scopes of "fake" locals named the same 
                'as the fields. These locals are not emitted to IL.
 
                '         vb\language\debugger\procedurecontext.cpp
                '  813                  // Since state machines lift (almost) all locals of a method, the lifted fields should
                '  814                  // only be shown in the debugger when the original local variable was in scope.  So
                '  815                  // we first check if there's a local by the given name and attempt to remove it from 
                '  816                  // m_localVariableMap.  If it was present, we decode the original local's name, otherwise
                '  817                  // we skip loading this lifted field since it is out of scope.
 
                _builder.AddLocalToScope(New LocalDefinition(
                    symbolOpt:=Nothing,
                    nameOpt:=field.Name,
                    type:=Nothing,
                    slot:=field.SlotIndex,
                    synthesizedKind:=SynthesizedLocalKind.EmitterTemp,
                    id:=Nothing,
                    pdbAttributes:=LocalVariableAttributes.None,
                    constraints:=LocalSlotConstraints.None,
                    dynamicTransformFlags:=Nothing,
                    tupleElementNames:=Nothing))
            Else
                _builder.DefineUserDefinedStateMachineHoistedLocal(field.SlotIndex)
            End If
        End Sub
 
        Private Sub EmitUnstructuredExceptionResumeSwitch(node As BoundUnstructuredExceptionResumeSwitch)
            ' Resume statements will branch here.  Just load the resume local and
            ' branch to the switch table
            EmitLabelStatement(node.ResumeLabel)
            EmitExpression(node.ResumeTargetTemporary, used:=True)
 
            Dim switchLabel As New Object()
            _builder.EmitBranch(ILOpCode.Br_s, switchLabel)
 
            _builder.AdjustStack(-1)
 
            ' Resume Next statements will branch here.  Increment the resume local and
            ' fall through to the switch table
            EmitLabelStatement(node.ResumeNextLabel)
            EmitExpression(node.ResumeTargetTemporary, used:=True)
            _builder.EmitIntConstant(1)
            _builder.EmitOpCode(ILOpCode.Add)
 
            ' now start generating the resume switch table
            _builder.MarkLabel(switchLabel)
 
            ' but first clear the resume local
            _builder.EmitIntConstant(0)
            _builder.EmitLocalStore(GetLocal(node.ResumeTargetTemporary))
 
            EmitSwitch(node.Jumps)
        End Sub
 
    End Class
 
End Namespace