Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -882,7 +882,7 @@ internal BoundStatement WrapWithVariablesAndLocalFunctionsIfAny(CSharpSyntaxNode
return statement;
}

return new BoundBlock(statement.Syntax, locals, localFunctions, hasUnsafeModifier: false,
return new BoundBlock(statement.Syntax, locals, localFunctions, hasUnsafeModifier: false, instrumentation: null,
ImmutableArray.Create(statement))
{ WasCompilerGenerated = true };
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1889,6 +1889,7 @@ private BoundBlock FinishBindBlockParts(CSharpSyntaxNode node, ImmutableArray<Bo
locals,
GetDeclaredLocalFunctionsForScope(node),
hasUnsafeModifier: node.Parent?.Kind() == SyntaxKind.UnsafeStatement,
instrumentation: null,
boundStatements);
}

Expand Down
17 changes: 17 additions & 0 deletions src/Compilers/CSharp/Portable/BoundTree/BoundNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -498,9 +498,26 @@ private void CheckDeclared(LocalSymbol local)

public override BoundNode? VisitBlock(BoundBlock node)
{
if (node.Instrumentation != null)
{
var added = DeclaredLocals.Add(node.Instrumentation.Local);
Debug.Assert(added);

_ = Visit(node.Instrumentation.Prologue);
}

AddAll(node.Locals);
base.VisitBlock(node);
RemoveAll(node.Locals);

if (node.Instrumentation != null)
{
_ = Visit(node.Instrumentation.Epilogue);

var removed = DeclaredLocals.Remove(node.Instrumentation.Local);
Debug.Assert(removed);
}

return null;
}

Expand Down
40 changes: 38 additions & 2 deletions src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -699,15 +699,48 @@
<Node Name="BoundTypeOfOperator" Base="BoundTypeOf">
<Field Name="SourceType" Type="BoundTypeExpression"/>
</Node>

<!-- Instrumentation info attached to BoundBlock -->
<Node Name="BoundBlockInstrumentation" Base="BoundNode">
<Field Name="Local" Type="LocalSymbol" Null="disallow"/>
<Field Name="Prologue" Type="BoundStatement" Null="disallow"/>
<Field Name="Epilogue" Type="BoundStatement" Null="disallow"/>
</Node>

<!-- Represents the raw metadata token index value for a method definition.
Used by dynamic instrumentation to index into tables or arrays of per-method information. -->
<!-- Represents the raw metadata RowId value for a method definition.
Used by dynamic instrumentation to index into tables or arrays of per-method information. -->
<Node Name="BoundMethodDefIndex" Base="BoundExpression">
<!-- Non-null type is required for this node kind -->
<Field Name="Type" Type="TypeSymbol" Override="true" Null="disallow"/>
<Field Name="Method" Type="MethodSymbol"/>
</Node>

<!-- Represents local id - a unique int value that represents local variable.-->
<Node Name="BoundLocalId" Base="BoundExpression">
<!-- Non-null type is required for this node kind -->
<Field Name="Type" Type="TypeSymbol" Override="true" Null="disallow"/>
<!-- Local symbol representing the variable. -->
<Field Name="Local" Type="LocalSymbol" Null="disallow"/>
<!-- Field symbol representing the variable if it has been lifted. -->
<Field Name="HoistedField" Type="FieldSymbol?" Null="allow"/>
</Node>

<!-- Represents parameter id - a unique int value that represents parameter.-->
<Node Name="BoundParameterId" Base="BoundExpression">
<!-- Non-null type is required for this node kind -->
<Field Name="Type" Type="TypeSymbol" Override="true" Null="disallow"/>
<!-- Local symbol representing the variable. -->
<Field Name="Parameter" Type="ParameterSymbol" Null="disallow"/>
<!-- Field symbol representing the parameter if it has been lifted. -->
<Field Name="HoistedField" Type="FieldSymbol?" Null="allow"/>
</Node>

<!-- Represents state machine instance id - a unique ulong value that represents an instance of a state machine.-->
<Node Name="BoundStateMachineInstanceId" Base="BoundExpression">
<!-- Non-null type is required for this node kind -->
<Field Name="Type" Type="TypeSymbol" Override="true" Null="disallow"/>
</Node>

<!-- Represents the maximum raw metadata token index value for any method definition in the current module. -->
<Node Name="BoundMaximumMethodDefIndex" Base="BoundExpression">
<!-- Non-null type is required for this node kind -->
Expand Down Expand Up @@ -974,6 +1007,9 @@
<Field Name="Locals" Type="ImmutableArray&lt;LocalSymbol&gt;"/>
<Field Name="LocalFunctions" Type="ImmutableArray&lt;LocalFunctionSymbol&gt;"/>
<Field Name="HasUnsafeModifier" Type="bool" Null="NotApplicable"/>

<!-- Optional block-level instrumentation info -->
<Field Name="Instrumentation" Type="BoundBlockInstrumentation?" Null="allow" />
</Node>

<!--
Expand Down
3 changes: 2 additions & 1 deletion src/Compilers/CSharp/Portable/BoundTree/Constructors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -595,7 +595,8 @@ public BoundGotoStatement(SyntaxNode syntax, LabelSymbol label, bool hasErrors =

internal partial class BoundBlock
{
public BoundBlock(SyntaxNode syntax, ImmutableArray<LocalSymbol> locals, ImmutableArray<BoundStatement> statements, bool hasErrors = false) : this(syntax, locals, ImmutableArray<LocalFunctionSymbol>.Empty, hasUnsafeModifier: false, statements, hasErrors)
public BoundBlock(SyntaxNode syntax, ImmutableArray<LocalSymbol> locals, ImmutableArray<BoundStatement> statements, bool hasErrors = false)
: this(syntax, locals, ImmutableArray<LocalFunctionSymbol>.Empty, hasUnsafeModifier: false, instrumentation: null, statements, hasErrors)
{
}

Expand Down
6 changes: 4 additions & 2 deletions src/Compilers/CSharp/Portable/CodeGen/CodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -348,12 +348,14 @@ private void EmitSymbolToken(TypeSymbol symbol, SyntaxNode syntaxNode)

private void EmitSymbolToken(MethodSymbol method, SyntaxNode syntaxNode, BoundArgListOperator optArgList, bool encodeAsRawDefinitionToken = false)
{
_builder.EmitToken(_module.Translate(method, syntaxNode, _diagnostics.DiagnosticBag, optArgList, needDeclaration: encodeAsRawDefinitionToken), syntaxNode, _diagnostics.DiagnosticBag, encodeAsRawDefinitionToken);
var methodRef = _module.Translate(method, syntaxNode, _diagnostics.DiagnosticBag, optArgList, needDeclaration: encodeAsRawDefinitionToken);
_builder.EmitToken(methodRef, syntaxNode, _diagnostics.DiagnosticBag, encodeAsRawDefinitionToken ? Cci.MetadataWriter.RawTokenEncoding.RowId : 0);
}

private void EmitSymbolToken(FieldSymbol symbol, SyntaxNode syntaxNode)
{
_builder.EmitToken(_module.Translate(symbol, syntaxNode, _diagnostics.DiagnosticBag), syntaxNode, _diagnostics.DiagnosticBag);
var fieldRef = _module.Translate(symbol, syntaxNode, _diagnostics.DiagnosticBag);
_builder.EmitToken(fieldRef, syntaxNode, _diagnostics.DiagnosticBag);
}

private void EmitSignatureToken(FunctionPointerTypeSymbol symbol, SyntaxNode syntaxNode)
Expand Down
47 changes: 47 additions & 0 deletions src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,16 @@ private void EmitExpressionCore(BoundExpression expression, bool used)
EmitSourceDocumentIndex((BoundSourceDocumentIndex)expression);
break;

case BoundKind.LocalId:
Debug.Assert(used);
EmitLocalIdExpression((BoundLocalId)expression);
break;

case BoundKind.ParameterId:
Debug.Assert(used);
EmitParameterIdExpression((BoundParameterId)expression);
break;

case BoundKind.MethodInfo:
if (used)
{
Expand Down Expand Up @@ -3310,6 +3320,43 @@ private void EmitMethodDefIndexExpression(BoundMethodDefIndex node)
EmitSymbolToken(symbol, node.Syntax, null, encodeAsRawDefinitionToken: true);
}

private void EmitLocalIdExpression(BoundLocalId node)
{
Debug.Assert(node.Type.SpecialType == SpecialType.System_Int32);

if (node.HoistedField is null)
{
_builder.EmitIntConstant(GetLocal(node.Local).SlotIndex);
}
else
{
EmitHoistedVariableId(node.HoistedField, node.Syntax);
}
}

private void EmitParameterIdExpression(BoundParameterId node)
{
Debug.Assert(node.Type.SpecialType == SpecialType.System_Int32);

if (node.HoistedField is null)
{
_builder.EmitIntConstant(node.Parameter.Ordinal);
}
else
{
EmitHoistedVariableId(node.HoistedField, node.Syntax);
}
}

private void EmitHoistedVariableId(FieldSymbol field, SyntaxNode syntax)
{
Debug.Assert(field.IsDefinition);
var fieldRef = _module.Translate(field, syntax, _diagnostics.DiagnosticBag, needDeclaration: true);

_builder.EmitOpCode(ILOpCode.Ldtoken);
_builder.EmitToken(fieldRef, syntax, _diagnostics.DiagnosticBag, Cci.MetadataWriter.RawTokenEncoding.LiftedVariableId);
}

private void EmitMaximumMethodDefIndexExpression(BoundMaximumMethodDefIndex node)
{
Debug.Assert(node.Type.SpecialType == SpecialType.System_Int32);
Expand Down
58 changes: 57 additions & 1 deletion src/Compilers/CSharp/Portable/CodeGen/EmitStatement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,54 @@ private bool IsLastBlockInMethod(BoundBlock block)
}

private void EmitBlock(BoundBlock block)
{
if (block.Instrumentation is not null)
{
EmitInstrumentedBlock(block.Instrumentation, block);
}
else
{
EmitUninstrumentedBlock(block);
}
}

private void EmitInstrumentedBlock(BoundBlockInstrumentation instrumentation, BoundBlock block)
{
_builder.OpenLocalScope();
DefineLocal(instrumentation.Local, block.Syntax);

if (_emitPdbSequencePoints)
{
EmitHiddenSequencePoint();
}

EmitStatement(instrumentation.Prologue);

_builder.AssertStackEmpty();

_builder.OpenLocalScope(ScopeType.TryCatchFinally);

_builder.OpenLocalScope(ScopeType.Try);
EmitUninstrumentedBlock(block);
_builder.CloseLocalScope(); // try

_builder.OpenLocalScope(ScopeType.Finally);

if (_emitPdbSequencePoints)
{
EmitHiddenSequencePoint();
}

EmitStatement(instrumentation.Epilogue);
_builder.CloseLocalScope(); // finally

_builder.CloseLocalScope(); // try-finally

FreeLocal(instrumentation.Local);
_builder.CloseLocalScope();
}

private void EmitUninstrumentedBlock(BoundBlock block)
{
var hasLocals = !block.Locals.IsEmpty;

Expand All @@ -644,7 +692,15 @@ private void EmitBlock(BoundBlock block)
if (_indirectReturnState == IndirectReturnState.Needed &&
IsLastBlockInMethod(block))
{
HandleReturn();
if (block.Instrumentation != null)
{
// jump out of try-finally
_builder.EmitBranch(ILOpCode.Br, s_returnLabel);
}
else
{
HandleReturn();
}
}

if (hasLocals)
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,11 @@ public override BoundNode VisitBlock(BoundBlock node)
{
Debug.Assert(EvalStackIsEmpty(), "entering blocks when evaluation stack is not empty?");

if (node.Instrumentation != null)
{
DeclareLocal(node.Instrumentation.Local, stack: 0);
}

// normally we would not allow stack locals
// when evaluation stack is not empty.
DeclareLocals(node.Locals, 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2791,7 +2791,6 @@ private void GetDiagnosticsForAllMethodBodies(BindingDiagnosticBag diagnostics,
cancellationToken: cancellationToken)
: null,
emittingPdb: false,
emitTestCoverageData: false,
hasDeclarationErrors: false,
emitMethodBodies: false,
diagnostics: diagnostics,
Expand Down Expand Up @@ -2905,7 +2904,6 @@ void compileMethodBodiesAndDocComments(SyntaxTree? filterTree, TextSpan? filterS
compilation: this,
moduleBeingBuiltOpt: null,
emittingPdb: false,
emitTestCoverageData: false,
hasDeclarationErrors: false,
emitMethodBodies: false,
diagnostics: bindingDiagnostics,
Expand Down Expand Up @@ -3191,12 +3189,12 @@ internal override StrongNameKeys StrongNameKeys
internal override bool CompileMethods(
CommonPEModuleBuilder moduleBuilder,
bool emittingPdb,
bool emitMetadataOnly,
bool emitTestCoverageData,
DiagnosticBag diagnostics,
Predicate<ISymbolInternal>? filterOpt,
CancellationToken cancellationToken)
{
var emitMetadataOnly = moduleBuilder.EmitOptions.EmitMetadataOnly;

// The diagnostics should include syntax and declaration errors. We insert these before calling Emitter.Emit, so that the emitter
// does not attempt to emit if there are declaration errors (but we do insert all errors from method body binding...)
PooledHashSet<int>? excludeDiagnostics = null;
Expand Down Expand Up @@ -3233,7 +3231,7 @@ internal override bool CompileMethods(
}
else
{
if ((emittingPdb || emitTestCoverageData) &&
if ((emittingPdb || moduleBeingBuilt.EmitOptions.InstrumentationKinds.Contains(InstrumentationKind.TestCoverage)) &&
!CreateDebugDocuments(moduleBeingBuilt.DebugDocumentsBuilder, moduleBeingBuilt.EmbeddedTexts, diagnostics))
{
return false;
Expand All @@ -3251,7 +3249,6 @@ internal override bool CompileMethods(
this,
moduleBeingBuilt,
emittingPdb,
emitTestCoverageData,
hasDeclarationErrors,
emitMethodBodies: true,
diagnostics: new BindingDiagnosticBag(methodBodyDiagnosticBag),
Expand Down
Loading