private void LowerDecisionTree(BoundExpression expression, DecisionTree decisionTree) { if (decisionTree == null) { return; } // If the input expression was a constant or a simple read of a local, then that is the // decision tree's expression. Otherwise it is a newly created temp, to which we must // assign the switch expression. if (decisionTree.Temp != null) { // Store the input expression into a temp if (decisionTree.Expression != expression) { var convertedExpression = _factory.Convert(decisionTree.Expression.Type, expression); _loweredDecisionTree.Add(_factory.Assignment(decisionTree.Expression, convertedExpression)); } if (_declaredTempSet.Add(decisionTree.Temp)) { _declaredTemps.Add(decisionTree.Temp); } else { // we should only attempt to declare each temp once. throw ExceptionUtilities.Unreachable; } } switch (decisionTree.Kind) { case DecisionTree.DecisionKind.ByType: { LowerDecisionTree((DecisionTree.ByType)decisionTree); return; } case DecisionTree.DecisionKind.ByValue: { LowerDecisionTree((DecisionTree.ByValue)decisionTree); return; } case DecisionTree.DecisionKind.Guarded: { LowerDecisionTree((DecisionTree.Guarded)decisionTree); return; } default: throw ExceptionUtilities.UnexpectedValue(decisionTree.Kind); } }
/// <summary> /// <![CDATA[ /// fixed(int* ptr = &v){ ... } == becomes ===> /// /// pinned ref int pinnedTemp = ref v; // pinning managed ref /// int* ptr = (int*)&pinnedTemp; // unsafe cast to unmanaged ptr /// . . . /// ]]> /// </summary> private BoundStatement InitializeFixedStatementRegularLocal( BoundLocalDeclaration localDecl, LocalSymbol localSymbol, BoundFixedLocalCollectionInitializer fixedInitializer, SyntheticBoundNodeFactory factory, out LocalSymbol pinnedTemp) { TypeSymbol localType = localSymbol.Type; BoundExpression initializerExpr = VisitExpression(fixedInitializer.Expression); // initializer expr should be either an address(&) of something or a fixed field access. // either should lower into addressof Debug.Assert(initializerExpr.Kind == BoundKind.AddressOfOperator); TypeSymbol initializerType = ((PointerTypeSymbol)initializerExpr.Type).PointedAtType; // initializer expressions are bound/lowered right into addressof operators here // that is a bit too far // we need to pin the underlying field, and only then take the address. initializerExpr = ((BoundAddressOfOperator)initializerExpr).Operand; // intervening parens may have been skipped by the binder; find the declarator VariableDeclaratorSyntax declarator = fixedInitializer.Syntax.FirstAncestorOrSelf <VariableDeclaratorSyntax>(); Debug.Assert(declarator != null); pinnedTemp = factory.SynthesizedLocal( initializerType, syntax: declarator, isPinned: true, //NOTE: different from the array and string cases // RefReadOnly to allow referring to readonly variables. (technically we only "read" through the temp anyways) refKind: RefKind.RefReadOnly, kind: SynthesizedLocalKind.FixedReference); // NOTE: we pin the reference, not the pointer. Debug.Assert(pinnedTemp.IsPinned); Debug.Assert(!localSymbol.IsPinned); // pinnedTemp = ref v; BoundStatement pinnedTempInit = factory.Assignment(factory.Local(pinnedTemp), initializerExpr, isRef: true); // &pinnedTemp var addr = new BoundAddressOfOperator( factory.Syntax, factory.Local(pinnedTemp), type: fixedInitializer.ElementPointerType); // (int*)&pinnedTemp var pointerValue = factory.Convert( localType, addr, fixedInitializer.ElementPointerTypeConversion); // ptr = (int*)&pinnedTemp; BoundStatement localInit = InstrumentLocalDeclarationIfNecessary(localDecl, localSymbol, factory.Assignment(factory.Local(localSymbol), pointerValue)); return(factory.Block(pinnedTempInit, localInit)); }
private BoundStatement InitializeFixedStatementStringLocal( BoundLocalDeclaration localDecl, LocalSymbol localSymbol, BoundFixedLocalCollectionInitializer fixedInitializer, SyntheticBoundNodeFactory factory, out LocalSymbol stringTemp, out LocalSymbol localToClear) { TypeSymbol localType = localSymbol.Type; BoundExpression initializerExpr = VisitExpression(fixedInitializer.Expression); TypeSymbol initializerType = initializerExpr.Type; // intervening parens may have been skipped by the binder; find the declarator VariableDeclaratorSyntax declarator = fixedInitializer.Syntax.FirstAncestorOrSelf <VariableDeclaratorSyntax>(); Debug.Assert(declarator != null); stringTemp = factory.SynthesizedLocal(initializerType, syntax: declarator, isPinned: true, kind: SynthesizedLocalKind.FixedString); // NOTE: we pin the string, not the pointer. Debug.Assert(stringTemp.IsPinned); Debug.Assert(!localSymbol.IsPinned); BoundStatement stringTempInit = factory.Assignment(factory.Local(stringTemp), initializerExpr); var convertedStringTemp = factory.Convert( localType, factory.Local(stringTemp), fixedInitializer.ElementPointerTypeConversion); BoundStatement localInit = InstrumentLocalDeclarationIfNecessary(localDecl, localSymbol, factory.Assignment(factory.Local(localSymbol), convertedStringTemp)); BoundExpression notNullCheck = MakeNullCheck(factory.Syntax, factory.Local(localSymbol), BinaryOperatorKind.NotEqual); BoundExpression helperCall; MethodSymbol offsetMethod; if (TryGetWellKnownTypeMember(fixedInitializer.Syntax, WellKnownMember.System_Runtime_CompilerServices_RuntimeHelpers__get_OffsetToStringData, out offsetMethod)) { helperCall = factory.Call(receiver: null, method: offsetMethod); } else { helperCall = new BoundBadExpression(fixedInitializer.Syntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray <BoundNode> .Empty, ErrorTypeSymbol.UnknownResultType); } BoundExpression addition = factory.Binary(BinaryOperatorKind.PointerAndIntAddition, localType, factory.Local(localSymbol), helperCall); BoundStatement conditionalAdd = factory.If(notNullCheck, factory.Assignment(factory.Local(localSymbol), addition)); localToClear = stringTemp; return(factory.Block(stringTempInit, localInit, conditionalAdd)); }
private BoundExpression Visit(BoundExpression node) { if (node == null) { return(null); } CSharpSyntaxNode old = Bound.Syntax; Bound.Syntax = node.Syntax; var result = VisitInternal(node); Bound.Syntax = old; return(Bound.Convert(ExpressionType, result)); }
private static BoundStatement ConstructNullCheckHelperCall(ParameterSymbol parameter, ref MethodSymbol?throwIfNullMethod, SyntheticBoundNodeFactory factory) { if (throwIfNullMethod is null) { var module = factory.ModuleBuilderOpt !; var diagnosticSyntax = factory.CurrentFunction.GetNonNullSyntaxNode(); var diagnostics = factory.Diagnostics.DiagnosticBag; Debug.Assert(diagnostics is not null); throwIfNullMethod = module.EnsureThrowIfNullFunctionExists(diagnosticSyntax, factory, diagnostics); } var call = factory.Call( receiver: null, throwIfNullMethod, arg0: factory.Convert(factory.SpecialType(SpecialType.System_Object), factory.Parameter(parameter)), arg1: factory.StringLiteral(parameter.Name)); return(factory.HiddenSequencePoint(factory.ExpressionStatement(call))); }
private BoundStatement InitializeFixedStatementArrayLocal( LocalSymbol localSymbol, BoundFixedLocalCollectionInitializer fixedInitializer, SyntheticBoundNodeFactory factory, out LocalSymbol arrayTemp) { // From ExpressionBinder::BindPtrToArray: // (((temp = array) != null && temp.Length > 0) ? loc = &temp[0] : loc = null) // NOTE: The assignment needs to be inside the EK_QUESTIONMARK. See Whidbey bug #397859. // We can't do loc = (... ? ... : ...) since the CLR type of &temp[0] is a managed // pointer and null is a UIntPtr - which confuses the JIT. We can't just convert // &temp[0] to UIntPtr with a conv.u instruction because then if a GC occurs between // the time of the cast and the assignment to the local, we're toast. TypeSymbol localType = localSymbol.Type; BoundExpression initializerExpr = VisitExpression(fixedInitializer.Expression); TypeSymbol initializerType = initializerExpr.Type; arrayTemp = factory.SynthesizedLocal(initializerType); ArrayTypeSymbol arrayType = (ArrayTypeSymbol)arrayTemp.Type; TypeSymbol arrayElementType = arrayType.ElementType; int arrayRank = arrayType.Rank; // NOTE: we pin the pointer, not the array. Debug.Assert(!arrayTemp.IsPinned); Debug.Assert(localSymbol.IsPinned); //(temp = array) BoundExpression arrayTempInit = factory.AssignmentExpression(factory.Local(arrayTemp), initializerExpr); //(temp = array) != null BoundExpression notNullCheck = MakeNullCheck(factory.Syntax, arrayTempInit, BinaryOperatorKind.NotEqual); BoundExpression lengthCall; if (arrayRank == 1) { lengthCall = factory.ArrayLength(factory.Local(arrayTemp)); } else { MethodSymbol lengthMethod; if (TryGetWellKnownTypeMember(fixedInitializer.Syntax, WellKnownMember.System_Array__get_Length, out lengthMethod)) { lengthCall = factory.Call(factory.Local(arrayTemp), lengthMethod); } else { lengthCall = new BoundBadExpression(fixedInitializer.Syntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundNode>(factory.Local(arrayTemp)), ErrorTypeSymbol.UnknownResultType); } } // NOTE: dev10 comment says ">", but code actually checks "!=" //temp.Length != 0 BoundExpression lengthCheck = factory.Binary(BinaryOperatorKind.IntNotEqual, factory.SpecialType(SpecialType.System_Boolean), lengthCall, factory.Literal(0)); //((temp = array) != null && temp.Length != 0) BoundExpression condition = factory.Binary(BinaryOperatorKind.LogicalBoolAnd, factory.SpecialType(SpecialType.System_Boolean), notNullCheck, lengthCheck); //temp[0] BoundExpression firstElement = factory.ArrayAccessFirstElement(factory.Local(arrayTemp)); // NOTE: this is a fixed statement address-of in that it's the initial value of pinned local. //&temp[0] BoundExpression firstElementAddress = new BoundAddressOfOperator(factory.Syntax, firstElement, isFixedStatementAddressOf: true, type: new PointerTypeSymbol(arrayElementType)); BoundExpression convertedFirstElementAddress = factory.Convert( localType, firstElementAddress, fixedInitializer.ElementPointerTypeConversion); //loc = &temp[0] BoundExpression consequenceAssignment = factory.AssignmentExpression(factory.Local(localSymbol), convertedFirstElementAddress); //loc = null BoundExpression alternativeAssignment = factory.AssignmentExpression(factory.Local(localSymbol), factory.Null(localType)); //(((temp = array) != null && temp.Length != 0) ? loc = &temp[0] : loc = null) BoundStatement localInit = factory.ExpressionStatement( new BoundConditionalOperator(factory.Syntax, condition, consequenceAssignment, alternativeAssignment, ConstantValue.NotAvailable, localType)); return(AddLocalDeclarationSequencePointIfNecessary(fixedInitializer.Syntax.Parent.Parent, localSymbol, localInit)); }
private BoundStatement InitializeFixedStatementArrayLocal( LocalSymbol localSymbol, BoundFixedLocalCollectionInitializer fixedInitializer, SyntheticBoundNodeFactory factory, out LocalSymbol arrayTemp) { // From ExpressionBinder::BindPtrToArray: // (((temp = array) != null && temp.Length > 0) ? loc = &temp[0] : loc = null) // NOTE: The assignment needs to be inside the EK_QUESTIONMARK. See Whidbey bug #397859. // We can't do loc = (... ? ... : ...) since the CLR type of &temp[0] is a managed // pointer and null is a UIntPtr - which confuses the JIT. We can't just convert // &temp[0] to UIntPtr with a conv.u instruction because then if a GC occurs between // the time of the cast and the assignment to the local, we're toast. TypeSymbol localType = localSymbol.Type; BoundExpression initializerExpr = VisitExpression(fixedInitializer.Expression); TypeSymbol initializerType = initializerExpr.Type; arrayTemp = factory.SynthesizedLocal(initializerType); ArrayTypeSymbol arrayType = (ArrayTypeSymbol)arrayTemp.Type; TypeSymbol arrayElementType = arrayType.ElementType; int arrayRank = arrayType.Rank; // NOTE: we pin the pointer, not the array. Debug.Assert(!arrayTemp.IsPinned); Debug.Assert(localSymbol.IsPinned); //(temp = array) BoundExpression arrayTempInit = factory.AssignmentExpression(factory.Local(arrayTemp), initializerExpr); //(temp = array) != null BoundExpression notNullCheck = MakeNullCheck(factory.Syntax, arrayTempInit, BinaryOperatorKind.NotEqual); BoundExpression lengthCall; if (arrayRank == 1) { lengthCall = factory.ArrayLength(factory.Local(arrayTemp)); } else { MethodSymbol lengthMethod; if (TryGetWellKnownTypeMember(fixedInitializer.Syntax, WellKnownMember.System_Array__get_Length, out lengthMethod)) { lengthCall = factory.Call(factory.Local(arrayTemp), lengthMethod); } else { lengthCall = new BoundBadExpression(fixedInitializer.Syntax, LookupResultKind.NotInvocable, ImmutableArray<Symbol>.Empty, ImmutableArray.Create<BoundNode>(factory.Local(arrayTemp)), ErrorTypeSymbol.UnknownResultType); } } // NOTE: dev10 comment says ">", but code actually checks "!=" //temp.Length != 0 BoundExpression lengthCheck = factory.Binary(BinaryOperatorKind.IntNotEqual, factory.SpecialType(SpecialType.System_Boolean), lengthCall, factory.Literal(0)); //((temp = array) != null && temp.Length != 0) BoundExpression condition = factory.Binary(BinaryOperatorKind.LogicalBoolAnd, factory.SpecialType(SpecialType.System_Boolean), notNullCheck, lengthCheck); //temp[0] BoundExpression firstElement = factory.ArrayAccessFirstElement(factory.Local(arrayTemp)); // NOTE: this is a fixed statement address-of in that it's the initial value of pinned local. //&temp[0] BoundExpression firstElementAddress = new BoundAddressOfOperator(factory.Syntax, firstElement, isFixedStatementAddressOf: true, type: new PointerTypeSymbol(arrayElementType)); BoundExpression convertedFirstElementAddress = factory.Convert( localType, firstElementAddress, fixedInitializer.ElementPointerTypeConversion); //loc = &temp[0] BoundExpression consequenceAssignment = factory.AssignmentExpression(factory.Local(localSymbol), convertedFirstElementAddress); //loc = null BoundExpression alternativeAssignment = factory.AssignmentExpression(factory.Local(localSymbol), factory.Null(localType)); //(((temp = array) != null && temp.Length != 0) ? loc = &temp[0] : loc = null) BoundStatement localInit = factory.ExpressionStatement( new BoundConditionalOperator(factory.Syntax, condition, consequenceAssignment, alternativeAssignment, ConstantValue.NotAvailable, localType)); return AddLocalDeclarationSequencePointIfNecessary(fixedInitializer.Syntax.Parent.Parent, localSymbol, localInit); }
private BoundStatement InitializeFixedStatementStringLocal( LocalSymbol localSymbol, BoundFixedLocalCollectionInitializer fixedInitializer, SyntheticBoundNodeFactory factory, out LocalSymbol stringTemp, out LocalSymbol localToClear) { TypeSymbol localType = localSymbol.Type; BoundExpression initializerExpr = VisitExpression(fixedInitializer.Expression); TypeSymbol initializerType = initializerExpr.Type; // intervening parens may have been skipped by the binder; find the declarator VariableDeclaratorSyntax declarator = fixedInitializer.Syntax.FirstAncestorOrSelf<VariableDeclaratorSyntax>(); Debug.Assert(declarator != null); stringTemp = factory.SynthesizedLocal(initializerType, syntax: declarator, isPinned: true, kind: SynthesizedLocalKind.FixedString); // NOTE: we pin the string, not the pointer. Debug.Assert(stringTemp.IsPinned); Debug.Assert(!localSymbol.IsPinned); BoundStatement stringTempInit = factory.Assignment(factory.Local(stringTemp), initializerExpr); var convertedStringTemp = factory.Convert( localType, factory.Local(stringTemp), fixedInitializer.ElementPointerTypeConversion); BoundStatement localInit = AddLocalDeclarationSequencePointIfNecessary(declarator, localSymbol, factory.Assignment(factory.Local(localSymbol), convertedStringTemp)); BoundExpression notNullCheck = MakeNullCheck(factory.Syntax, factory.Local(stringTemp), BinaryOperatorKind.NotEqual); BoundExpression helperCall; MethodSymbol offsetMethod; if (TryGetWellKnownTypeMember(fixedInitializer.Syntax, WellKnownMember.System_Runtime_CompilerServices_RuntimeHelpers__get_OffsetToStringData, out offsetMethod)) { helperCall = factory.Call(receiver: null, method: offsetMethod); } else { helperCall = new BoundBadExpression(fixedInitializer.Syntax, LookupResultKind.NotInvocable, ImmutableArray<Symbol>.Empty, ImmutableArray<BoundNode>.Empty, ErrorTypeSymbol.UnknownResultType); } BoundExpression addition = factory.Binary(BinaryOperatorKind.PointerAndIntAddition, localType, factory.Local(localSymbol), helperCall); BoundStatement conditionalAdd = factory.If(notNullCheck, factory.Assignment(factory.Local(localSymbol), addition)); localToClear = stringTemp; return factory.Block(stringTempInit, localInit, conditionalAdd); }
public override BoundNode VisitLoweredConditionalAccess(BoundLoweredConditionalAccess node) { var receiverRefKind = ReceiverSpillRefKind(node.Receiver); BoundSpillSequenceBuilder receiverBuilder = null; var receiver = VisitExpression(ref receiverBuilder, node.Receiver); BoundSpillSequenceBuilder whenNotNullBuilder = null; var whenNotNull = VisitExpression(ref whenNotNullBuilder, node.WhenNotNull); BoundSpillSequenceBuilder whenNullBuilder = null; var whenNullOpt = VisitExpression(ref whenNullBuilder, node.WhenNullOpt); if (whenNotNullBuilder == null && whenNullBuilder == null) { return(UpdateExpression(receiverBuilder, node.Update(receiver, node.HasValueMethodOpt, whenNotNull, whenNullOpt, node.Id, node.Type))); } if (receiverBuilder == null) { receiverBuilder = new BoundSpillSequenceBuilder(); } if (whenNotNullBuilder == null) { whenNotNullBuilder = new BoundSpillSequenceBuilder(); } if (whenNullBuilder == null) { whenNullBuilder = new BoundSpillSequenceBuilder(); } BoundExpression condition; if (receiver.Type.IsReferenceType || receiver.Type.IsValueType || receiverRefKind == RefKind.None) { // spill to a clone receiver = Spill(receiverBuilder, receiver, RefKind.None); var hasValueOpt = node.HasValueMethodOpt; if (hasValueOpt == null) { condition = _F.ObjectNotEqual( _F.Convert(_F.SpecialType(SpecialType.System_Object), receiver), _F.Null(_F.SpecialType(SpecialType.System_Object))); } else { condition = _F.Call(receiver, hasValueOpt); } } else { Debug.Assert(node.HasValueMethodOpt == null); receiver = Spill(receiverBuilder, receiver, RefKind.Ref); var clone = _F.SynthesizedLocal(receiver.Type, _F.Syntax, refKind: RefKind.None, kind: SynthesizedLocalKind.Spill); receiverBuilder.AddLocal(clone); // (object)default(T) != null var isNotClass = _F.ObjectNotEqual( _F.Convert(_F.SpecialType(SpecialType.System_Object), _F.Default(receiver.Type)), _F.Null(_F.SpecialType(SpecialType.System_Object))); // isNotCalss || {clone = receiver; (object)clone != null} condition = _F.LogicalOr( isNotClass, _F.MakeSequence( _F.AssignmentExpression(_F.Local(clone), receiver), _F.ObjectNotEqual( _F.Convert(_F.SpecialType(SpecialType.System_Object), _F.Local(clone)), _F.Null(_F.SpecialType(SpecialType.System_Object)))) ); receiver = _F.ComplexConditionalReceiver(receiver, _F.Local(clone)); } if (node.Type.IsVoidType()) { var whenNotNullStatement = UpdateStatement(whenNotNullBuilder, _F.ExpressionStatement(whenNotNull)); whenNotNullStatement = ConditionalReceiverReplacer.Replace(whenNotNullStatement, receiver, node.Id, RecursionDepth); Debug.Assert(whenNullOpt == null || !LocalRewriter.ReadIsSideeffecting(whenNullOpt)); receiverBuilder.AddStatement(_F.If(condition, whenNotNullStatement)); return(receiverBuilder.Update(_F.Default(node.Type))); } else { var tmp = _F.SynthesizedLocal(node.Type, kind: SynthesizedLocalKind.Spill, syntax: _F.Syntax); var whenNotNullStatement = UpdateStatement(whenNotNullBuilder, _F.Assignment(_F.Local(tmp), whenNotNull)); whenNotNullStatement = ConditionalReceiverReplacer.Replace(whenNotNullStatement, receiver, node.Id, RecursionDepth); whenNullOpt = whenNullOpt ?? _F.Default(node.Type); receiverBuilder.AddLocal(tmp); receiverBuilder.AddStatement( _F.If(condition, whenNotNullStatement, UpdateStatement(whenNullBuilder, _F.Assignment(_F.Local(tmp), whenNullOpt)))); return(receiverBuilder.Update(_F.Local(tmp))); } }
/// <summary> /// <![CDATA[ /// fixed(int* ptr = arr){ ... } == becomes ===> /// /// pinned int[] pinnedTemp = arr; // pinning managed ref /// int* ptr = pinnedTemp != null && pinnedTemp.Length != 0 /// (int*)&pinnedTemp[0]: // unsafe cast to unmanaged ptr /// 0; /// . . . /// ]]> /// </summary> private BoundStatement InitializeFixedStatementArrayLocal( BoundLocalDeclaration localDecl, LocalSymbol localSymbol, BoundFixedLocalCollectionInitializer fixedInitializer, SyntheticBoundNodeFactory factory, out LocalSymbol pinnedTemp) { TypeSymbol localType = localSymbol.Type; BoundExpression initializerExpr = VisitExpression(fixedInitializer.Expression); TypeSymbol initializerType = initializerExpr.Type; pinnedTemp = factory.SynthesizedLocal(initializerType, isPinned: true); ArrayTypeSymbol arrayType = (ArrayTypeSymbol)pinnedTemp.Type; TypeWithAnnotations arrayElementType = arrayType.ElementTypeWithAnnotations; // NOTE: we pin the array, not the pointer. Debug.Assert(pinnedTemp.IsPinned); Debug.Assert(!localSymbol.IsPinned); //(pinnedTemp = array) BoundExpression arrayTempInit = factory.AssignmentExpression(factory.Local(pinnedTemp), initializerExpr); //(pinnedTemp = array) != null BoundExpression notNullCheck = MakeNullCheck(factory.Syntax, arrayTempInit, BinaryOperatorKind.NotEqual); BoundExpression lengthCall; if (arrayType.IsSZArray) { lengthCall = factory.ArrayLength(factory.Local(pinnedTemp)); } else { MethodSymbol lengthMethod; if (TryGetWellKnownTypeMember(fixedInitializer.Syntax, WellKnownMember.System_Array__get_Length, out lengthMethod)) { lengthCall = factory.Call(factory.Local(pinnedTemp), lengthMethod); } else { lengthCall = new BoundBadExpression(fixedInitializer.Syntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundExpression>(factory.Local(pinnedTemp)), ErrorTypeSymbol.UnknownResultType); } } // NOTE: dev10 comment says ">", but code actually checks "!=" //temp.Length != 0 BoundExpression lengthCheck = factory.Binary(BinaryOperatorKind.IntNotEqual, factory.SpecialType(SpecialType.System_Boolean), lengthCall, factory.Literal(0)); //((temp = array) != null && temp.Length != 0) BoundExpression condition = factory.Binary(BinaryOperatorKind.LogicalBoolAnd, factory.SpecialType(SpecialType.System_Boolean), notNullCheck, lengthCheck); //temp[0] BoundExpression firstElement = factory.ArrayAccessFirstElement(factory.Local(pinnedTemp)); // NOTE: this is a fixed statement address-of in that it's the initial value of the pointer. //&temp[0] BoundExpression firstElementAddress = new BoundAddressOfOperator(factory.Syntax, firstElement, type: new PointerTypeSymbol(arrayElementType)); BoundExpression convertedFirstElementAddress = factory.Convert( localType, firstElementAddress, fixedInitializer.ElementPointerTypeConversion); //loc = &temp[0] BoundExpression consequenceAssignment = factory.AssignmentExpression(factory.Local(localSymbol), convertedFirstElementAddress); //loc = null BoundExpression alternativeAssignment = factory.AssignmentExpression(factory.Local(localSymbol), factory.Null(localType)); //(((temp = array) != null && temp.Length != 0) ? loc = &temp[0] : loc = null) BoundStatement localInit = factory.ExpressionStatement( new BoundConditionalOperator(factory.Syntax, false, condition, consequenceAssignment, alternativeAssignment, ConstantValue.NotAvailable, localType)); return(InstrumentLocalDeclarationIfNecessary(localDecl, localSymbol, localInit)); }
/// <summary> /// <![CDATA[ /// fixed(int* ptr = &v){ ... } == becomes ===> /// /// pinned ref int pinnedTemp = ref v; // pinning managed ref /// int* ptr = (int*)&pinnedTemp; // unsafe cast to unmanaged ptr /// . . . /// ]]> /// </summary> private BoundStatement InitializeFixedStatementGetPinnable( BoundLocalDeclaration localDecl, LocalSymbol localSymbol, BoundFixedLocalCollectionInitializer fixedInitializer, SyntheticBoundNodeFactory factory, out LocalSymbol pinnedTemp) { TypeSymbol localType = localSymbol.Type; BoundExpression initializerExpr = VisitExpression(fixedInitializer.Expression); var initializerType = initializerExpr.Type; var initializerSyntax = initializerExpr.Syntax; var getPinnableMethod = fixedInitializer.GetPinnableOpt; // intervening parens may have been skipped by the binder; find the declarator VariableDeclaratorSyntax declarator = fixedInitializer.Syntax.FirstAncestorOrSelf <VariableDeclaratorSyntax>(); Debug.Assert(declarator != null); // pinned ref int pinnedTemp pinnedTemp = factory.SynthesizedLocal( getPinnableMethod.ReturnType, syntax: declarator, isPinned: true, //NOTE: different from the array and string cases // RefReadOnly to allow referring to readonly variables. (technically we only "read" through the temp anyways) refKind: RefKind.RefReadOnly, kind: SynthesizedLocalKind.FixedReference); BoundExpression callReceiver; int currentConditionalAccessID = 0; bool needNullCheck = !initializerType.IsValueType; if (needNullCheck) { currentConditionalAccessID = ++_currentConditionalAccessID; callReceiver = new BoundConditionalReceiver( initializerSyntax, currentConditionalAccessID, initializerType); } else { callReceiver = initializerExpr; } // .GetPinnable() var getPinnableCall = getPinnableMethod.IsStatic ? factory.Call(null, getPinnableMethod, callReceiver) : factory.Call(callReceiver, getPinnableMethod); // temp =ref .GetPinnable() var tempAssignment = factory.AssignmentExpression( factory.Local(pinnedTemp), getPinnableCall, isRef: true); // &pinnedTemp var addr = new BoundAddressOfOperator( factory.Syntax, factory.Local(pinnedTemp), type: fixedInitializer.ElementPointerType); // (int*)&pinnedTemp var pointerValue = factory.Convert( localType, addr, fixedInitializer.ElementPointerTypeConversion); // {pinnedTemp =ref .GetPinnable(), (int*)&pinnedTemp} BoundExpression pinAndGetPtr = factory.Sequence( locals: ImmutableArray <LocalSymbol> .Empty, sideEffects: ImmutableArray.Create <BoundExpression>(tempAssignment), result: pointerValue); if (needNullCheck) { // initializer?.{temp =ref .GetPinnable(), (int*)&pinnedTemp} ?? default; pinAndGetPtr = new BoundLoweredConditionalAccess( initializerSyntax, initializerExpr, hasValueMethodOpt: null, whenNotNull: pinAndGetPtr, whenNullOpt: null, // just return default(T*) currentConditionalAccessID, localType); } // ptr = initializer?.{temp =ref .GetPinnable(), (int*)&pinnedTemp} ?? default; BoundStatement localInit = InstrumentLocalDeclarationIfNecessary(localDecl, localSymbol, factory.Assignment(factory.Local(localSymbol), pinAndGetPtr)); return(localInit); }
/// <summary> /// Return the side-effect expression corresponding to an evaluation. /// </summary> protected BoundExpression LowerEvaluation(BoundDagEvaluation evaluation) { BoundExpression input = _tempAllocator.GetTemp(evaluation.Input); switch (evaluation) { case BoundDagFieldEvaluation f: { FieldSymbol field = f.Field; var outputTemp = new BoundDagTemp(f.Syntax, field.Type.TypeSymbol, f, index: 0); BoundExpression output = _tempAllocator.GetTemp(outputTemp); BoundExpression access = _localRewriter.MakeFieldAccess(f.Syntax, input, field, null, LookupResultKind.Viable, field.Type.TypeSymbol); access.WasCompilerGenerated = true; return(_factory.AssignmentExpression(output, access)); } case BoundDagPropertyEvaluation p: { PropertySymbol property = p.Property; var outputTemp = new BoundDagTemp(p.Syntax, property.Type.TypeSymbol, p, index: 0); BoundExpression output = _tempAllocator.GetTemp(outputTemp); return(_factory.AssignmentExpression(output, _factory.Property(input, property))); } case BoundDagDeconstructEvaluation d: { MethodSymbol method = d.DeconstructMethod; var refKindBuilder = ArrayBuilder <RefKind> .GetInstance(); var argBuilder = ArrayBuilder <BoundExpression> .GetInstance(); BoundExpression receiver; void addArg(RefKind refKind, BoundExpression expression) { refKindBuilder.Add(refKind); argBuilder.Add(expression); } Debug.Assert(method.Name == WellKnownMemberNames.DeconstructMethodName); int extensionExtra; if (method.IsStatic) { Debug.Assert(method.IsExtensionMethod); receiver = _factory.Type(method.ContainingType); addArg(method.ParameterRefKinds[0], input); extensionExtra = 1; } else { receiver = input; extensionExtra = 0; } for (int i = extensionExtra; i < method.ParameterCount; i++) { ParameterSymbol parameter = method.Parameters[i]; Debug.Assert(parameter.RefKind == RefKind.Out); var outputTemp = new BoundDagTemp(d.Syntax, parameter.Type.TypeSymbol, d, i - extensionExtra); addArg(RefKind.Out, _tempAllocator.GetTemp(outputTemp)); } return(_factory.Call(receiver, method, refKindBuilder.ToImmutableAndFree(), argBuilder.ToImmutableAndFree())); } case BoundDagTypeEvaluation t: { TypeSymbol inputType = input.Type; if (inputType.IsDynamic() || inputType.ContainsTypeParameter()) { inputType = _factory.SpecialType(SpecialType.System_Object); } TypeSymbol type = t.Type; var outputTemp = new BoundDagTemp(t.Syntax, type, t, index: 0); BoundExpression output = _tempAllocator.GetTemp(outputTemp); HashSet <DiagnosticInfo> useSiteDiagnostics = null; Conversion conversion = _factory.Compilation.Conversions.ClassifyBuiltInConversion(inputType, output.Type, ref useSiteDiagnostics); _localRewriter._diagnostics.Add(t.Syntax, useSiteDiagnostics); BoundExpression evaluated; if (conversion.Exists) { if (conversion.Kind == ConversionKind.ExplicitNullable && inputType.GetNullableUnderlyingType().Equals(output.Type, TypeCompareKind.AllIgnoreOptions) && _localRewriter.TryGetNullableMethod(t.Syntax, inputType, SpecialMember.System_Nullable_T_GetValueOrDefault, out MethodSymbol getValueOrDefault)) { // As a special case, since the null test has already been done we can use Nullable<T>.GetValueOrDefault evaluated = _factory.Call(input, getValueOrDefault); } else { evaluated = _factory.Convert(type, input, conversion); } } else { evaluated = _factory.As(input, type); } return(_factory.AssignmentExpression(output, evaluated)); } case BoundDagIndexEvaluation e: { // This is an evaluation of an indexed property with a constant int value. // The input type must be ITuple, and the property must be a property of ITuple. Debug.Assert(e.Property.ContainingSymbol.Equals(input.Type)); Debug.Assert(e.Property.GetMethod.ParameterCount == 1); Debug.Assert(e.Property.GetMethod.Parameters[0].Type.SpecialType == SpecialType.System_Int32); TypeSymbol type = e.Property.GetMethod.ReturnType.TypeSymbol; var outputTemp = new BoundDagTemp(e.Syntax, type, e, index: 0); BoundExpression output = _tempAllocator.GetTemp(outputTemp); return(_factory.AssignmentExpression(output, _factory.Call(input, e.Property.GetMethod, _factory.Literal(e.Index)))); } default: throw ExceptionUtilities.UnexpectedValue(evaluation); } }
private void LowerDecisionTree(DecisionTree.ByType byType) { var inputConstant = byType.Expression.ConstantValue; if (inputConstant != null) { if (inputConstant.IsNull) { // input is the constant null LowerDecisionTree(byType.Expression, byType.WhenNull); if (byType.WhenNull?.MatchIsComplete != true) { LowerDecisionTree(byType.Expression, byType.Default); } } else { // input is a non-null constant foreach (var kvp in byType.TypeAndDecision) { LowerDecisionTree(byType.Expression, kvp.Value); if (kvp.Value.MatchIsComplete) { return; } } LowerDecisionTree(byType.Expression, byType.Default); } } else { var defaultLabel = _factory.GenerateLabel("byTypeDefault"); // input is not a constant if (byType.Type.CanContainNull()) { // first test for null var notNullLabel = _factory.GenerateLabel("notNull"); var inputExpression = byType.Expression; var objectType = _factory.SpecialType(SpecialType.System_Object); var nullValue = _factory.Null(objectType); BoundExpression notNull = byType.Type.IsNullableType() ? _localRewriter.RewriteNullableNullEquality(_factory.Syntax, BinaryOperatorKind.NullableNullNotEqual, byType.Expression, nullValue, _factory.SpecialType(SpecialType.System_Boolean)) : _factory.ObjectNotEqual(nullValue, _factory.Convert(objectType, byType.Expression)); _loweredDecisionTree.Add(_factory.ConditionalGoto(notNull, notNullLabel, true)); LowerDecisionTree(byType.Expression, byType.WhenNull); if (byType.WhenNull?.MatchIsComplete != true) { _loweredDecisionTree.Add(_factory.Goto(defaultLabel)); } _loweredDecisionTree.Add(_factory.Label(notNullLabel)); } else { Debug.Assert(byType.WhenNull == null); } foreach (var td in byType.TypeAndDecision) { // then test for each type, sequentially var type = td.Key; var decision = td.Value; var failLabel = _factory.GenerateLabel("failedDecision"); var testAndCopy = TypeTestAndCopyToTemp(byType.Expression, decision.Expression); _loweredDecisionTree.Add(_factory.ConditionalGoto(testAndCopy, failLabel, false)); LowerDecisionTree(decision.Expression, decision); _loweredDecisionTree.Add(_factory.Label(failLabel)); } // finally, the default for when no type matches _loweredDecisionTree.Add(_factory.Label(defaultLabel)); LowerDecisionTree(byType.Expression, byType.Default); } }
protected BoundExpression LowerEvaluation(BoundDagEvaluation evaluation) { BoundExpression input = _tempAllocator.GetTemp(evaluation.Input); switch (evaluation) { case BoundDagFieldEvaluation f: { FieldSymbol field = f.Field; var outputTemp = new BoundDagTemp(f.Syntax, field.Type, f); BoundExpression output = _tempAllocator.GetTemp(outputTemp); BoundExpression access = _localRewriter.MakeFieldAccess(f.Syntax, input, field, null, LookupResultKind.Viable, field.Type); access.WasCompilerGenerated = true; return(_factory.AssignmentExpression(output, access)); } case BoundDagPropertyEvaluation p: { PropertySymbol property = p.Property; var outputTemp = new BoundDagTemp(p.Syntax, property.Type, p); BoundExpression output = _tempAllocator.GetTemp(outputTemp); return(_factory.AssignmentExpression(output, _localRewriter.MakePropertyAccess(_factory.Syntax, input, property, LookupResultKind.Viable, property.Type, isLeftOfAssignment: false))); } case BoundDagDeconstructEvaluation d: { MethodSymbol method = d.DeconstructMethod; var refKindBuilder = ArrayBuilder <RefKind> .GetInstance(); var argBuilder = ArrayBuilder <BoundExpression> .GetInstance(); BoundExpression receiver; void addArg(RefKind refKind, BoundExpression expression) { refKindBuilder.Add(refKind); argBuilder.Add(expression); } Debug.Assert(method.Name == WellKnownMemberNames.DeconstructMethodName); int extensionExtra; if (method.IsStatic) { Debug.Assert(method.IsExtensionMethod); receiver = _factory.Type(method.ContainingType); addArg(method.ParameterRefKinds[0], input); extensionExtra = 1; } else { receiver = input; extensionExtra = 0; } for (int i = extensionExtra; i < method.ParameterCount; i++) { ParameterSymbol parameter = method.Parameters[i]; Debug.Assert(parameter.RefKind == RefKind.Out); var outputTemp = new BoundDagTemp(d.Syntax, parameter.Type, d, i - extensionExtra); addArg(RefKind.Out, _tempAllocator.GetTemp(outputTemp)); } return(_factory.Call(receiver, method, refKindBuilder.ToImmutableAndFree(), argBuilder.ToImmutableAndFree())); } case BoundDagTypeEvaluation t: { TypeSymbol inputType = input.Type; Debug.Assert(inputType is { }); if (inputType.IsDynamic()) { // Avoid using dynamic conversions for pattern-matching. inputType = _factory.SpecialType(SpecialType.System_Object); input = _factory.Convert(inputType, input); } TypeSymbol type = t.Type; var outputTemp = new BoundDagTemp(t.Syntax, type, t); BoundExpression output = _tempAllocator.GetTemp(outputTemp); CompoundUseSiteInfo <AssemblySymbol> useSiteInfo = _localRewriter.GetNewCompoundUseSiteInfo(); Conversion conversion = _factory.Compilation.Conversions.ClassifyBuiltInConversion(inputType, output.Type, isChecked: false, ref useSiteInfo); Debug.Assert(!conversion.IsUserDefined); _localRewriter._diagnostics.Add(t.Syntax, useSiteInfo); BoundExpression evaluated; if (conversion.Exists) { if (conversion.Kind == ConversionKind.ExplicitNullable && inputType.GetNullableUnderlyingType().Equals(output.Type, TypeCompareKind.AllIgnoreOptions) && _localRewriter.TryGetNullableMethod(t.Syntax, inputType, SpecialMember.System_Nullable_T_GetValueOrDefault, out MethodSymbol getValueOrDefault)) { // As a special case, since the null test has already been done we can use Nullable<T>.GetValueOrDefault evaluated = _factory.Call(input, getValueOrDefault); } else { evaluated = _factory.Convert(type, input, conversion); } } else { evaluated = _factory.As(input, type); } return(_factory.AssignmentExpression(output, evaluated)); }
// For switch statements, we have an option of completely rewriting the switch header // and switch sections into simpler constructs, i.e. we can rewrite the switch header // using bound conditional goto statements and the rewrite the switch sections into // bound labeled statements. // // However, all the logic for emitting the switch jump tables is language agnostic // and includes IL optimizations. Hence we delay the switch jump table generation // till the emit phase. This way we also get additional benefit of sharing this code // between both VB and C# compilers. // // For string switch statements, we need to determine if we are generating a hash // table based jump table or a non hash jump table, i.e. linear string comparisons // with each case label. We use the Dev10 Heuristic to determine this // (see SwitchStringJumpTableEmitter.ShouldGenerateHashTableSwitch() for details). // If we are generating a hash table based jump table, we use a simple // hash function to hash the string constants corresponding to the case labels. // See SwitchStringJumpTableEmitter.ComputeStringHash(). // We need to emit this same function to compute the hash value into the compiler generated // <PrivateImplementationDetails> class. // If we have at least one string switch statement in a module that needs a // hash table based jump table, we generate a single public string hash synthesized method // that is shared across the module. private void LowerBasicSwitch(DecisionTree.ByValue byValue) { var switchSections = ArrayBuilder <BoundSwitchSection> .GetInstance(); var noValueMatches = _factory.GenerateLabel("noValueMatches"); var underlyingSwitchType = byValue.Type.IsEnumType() ? byValue.Type.GetEnumUnderlyingType() : byValue.Type; foreach (var vd in byValue.ValueAndDecision) { var value = vd.Key; var decision = vd.Value; var constantValue = ConstantValue.Create(value, underlyingSwitchType.SpecialType); var constantExpression = new BoundLiteral(_factory.Syntax, constantValue, underlyingSwitchType); var label = _factory.GenerateLabel("case+" + value); var switchLabel = new BoundSwitchLabel(_factory.Syntax, label, constantExpression, constantValue); var forValue = ArrayBuilder <BoundStatement> .GetInstance(); LowerDecisionTree(byValue.Expression, decision, forValue); if (!decision.MatchIsComplete) { forValue.Add(_factory.Goto(noValueMatches)); } var section = new BoundSwitchSection(_factory.Syntax, ImmutableArray.Create(switchLabel), forValue.ToImmutableAndFree()); switchSections.Add(section); } var rewrittenSections = switchSections.ToImmutableAndFree(); MethodSymbol stringEquality = null; if (underlyingSwitchType.SpecialType == SpecialType.System_String) { LocalRewriter.EnsureStringHashFunction(rewrittenSections, _factory.Syntax); stringEquality = LocalRewriter.GetSpecialTypeMethod(_factory.Syntax, SpecialMember.System_String__op_Equality); } // The BoundSwitchStatement requires a constant target when there are no sections, so we accomodate that here. var constantTarget = rewrittenSections.IsEmpty ? noValueMatches : null; var switchStatement = new BoundSwitchStatement( _factory.Syntax, null, _factory.Convert(underlyingSwitchType, byValue.Expression), constantTarget, ImmutableArray <LocalSymbol> .Empty, ImmutableArray <LocalFunctionSymbol> .Empty, rewrittenSections, noValueMatches, stringEquality); // The bound switch statement implicitly defines the label noValueMatches at the end, so we do not add it explicitly. switch (underlyingSwitchType.SpecialType) { case SpecialType.System_Boolean: // boolean switch is handled in LowerBooleanSwitch, not here. throw ExceptionUtilities.Unreachable; case SpecialType.System_String: case SpecialType.System_Byte: case SpecialType.System_Char: case SpecialType.System_Int16: case SpecialType.System_Int32: case SpecialType.System_Int64: case SpecialType.System_SByte: case SpecialType.System_UInt16: case SpecialType.System_UInt32: case SpecialType.System_UInt64: { // emit knows how to efficiently generate code for these kinds of switches. _loweredDecisionTree.Add(switchStatement); break; } default: { // other types, such as float, double, and decimal, are not currently // handled by emit and must be lowered here. _loweredDecisionTree.Add(LowerNonprimitiveSwitch(switchStatement)); break; } } LowerDecisionTree(byValue.Expression, byValue.Default); }