diff --git a/src/MLIR/Semantics/AttributeValue.cs b/src/MLIR/Semantics/AttributeValue.cs index 65b6c7c..56c024d 100644 --- a/src/MLIR/Semantics/AttributeValue.cs +++ b/src/MLIR/Semantics/AttributeValue.cs @@ -11,7 +11,7 @@ public abstract class AttributeValue /// /// Initializes a new instance of the class. /// - protected AttributeValue(RawSyntaxText syntax, string? name, AttributeDefinition? definition, SourceLocation location) + protected AttributeValue(AttributeValueSyntax? syntax, string? name, AttributeDefinition? definition, SourceLocation location) { Syntax = syntax; Name = name; @@ -20,9 +20,9 @@ protected AttributeValue(RawSyntaxText syntax, string? name, AttributeDefinition } /// - /// Gets the raw syntax text for the attribute value. + /// Gets the syntax for the attribute value, or null if this is a synthetic attribute value with no corresponding source text. /// - public RawSyntaxText Syntax { get; } + public AttributeValueSyntax? Syntax { get; } /// /// Gets the canonical attribute name, if one was recognized. diff --git a/src/MLIR/Semantics/AttributeValueConstructionContext.cs b/src/MLIR/Semantics/AttributeValueConstructionContext.cs index 2d81498..169e9ca 100644 --- a/src/MLIR/Semantics/AttributeValueConstructionContext.cs +++ b/src/MLIR/Semantics/AttributeValueConstructionContext.cs @@ -8,7 +8,7 @@ namespace MLIR.Semantics; /// public sealed class AttributeValueConstructionContext { - internal AttributeValueConstructionContext(RawSyntaxText syntax, string? name, AttributeDefinition definition, SourceLocation location) + internal AttributeValueConstructionContext(AttributeValueSyntax syntax, string? name, AttributeDefinition definition, SourceLocation location) { Syntax = syntax; Name = name; @@ -17,9 +17,9 @@ internal AttributeValueConstructionContext(RawSyntaxText syntax, string? name, A } /// - /// Gets the raw syntax text for the attribute value. + /// Gets the syntax for the attribute value. /// - public RawSyntaxText Syntax { get; } + public AttributeValueSyntax Syntax { get; } /// /// Gets the canonical attribute name, if one was recognized. diff --git a/src/MLIR/Semantics/Binder.cs b/src/MLIR/Semantics/Binder.cs index b5d5733..268c542 100644 --- a/src/MLIR/Semantics/Binder.cs +++ b/src/MLIR/Semantics/Binder.cs @@ -319,12 +319,12 @@ public AttributeValue BindAttributeValue(RawSyntaxText syntax) var location = syntax.Location; if (definition != null) { - attribute = definition.Factory(new AttributeValueConstructionContext(syntax, canonicalName, definition, location)); + attribute = definition.Factory(new AttributeValueConstructionContext(new RawAttributeValueSyntax(syntax), canonicalName, definition, location)); definition.AssemblyFormat?.Bind(attribute, new AttributeAssemblyBindingContext(attribute, diagnostics)); } else { - attribute = new UnknownAttributeValue(syntax, canonicalName, definition, location); + attribute = new UnknownAttributeValue(new RawAttributeValueSyntax(syntax), canonicalName, definition, location); } return attribute; diff --git a/src/MLIR/Semantics/Block.cs b/src/MLIR/Semantics/Block.cs index 8da58a9..53f9023 100644 --- a/src/MLIR/Semantics/Block.cs +++ b/src/MLIR/Semantics/Block.cs @@ -6,38 +6,61 @@ namespace MLIR.Semantics; /// /// Represents a semantic block within a region. /// -/// -/// Initializes a new instance of the class. -/// -/// The concrete syntax node for the block. -/// The semantic block arguments. -/// The operations contained in the block. -public sealed class Block(BlockSyntax syntax, IReadOnlyList arguments, IReadOnlyList operations) +public sealed class Block { /// - /// Gets the concrete syntax node for the block. + /// Initializes a new instance of the class from a concrete syntax node. + /// The block label reference is derived from the syntax node's label token. /// - public BlockSyntax Syntax { get; } = syntax; + /// The concrete syntax node for the block. + /// The semantic block arguments. + /// The operations contained in the block. + public Block(BlockSyntax syntax, IReadOnlyList arguments, IReadOnlyList operations) + { + Syntax = syntax; + LabelReference = new BlockReference(syntax.LabelToken); + Arguments = arguments; + Operations = operations; + } /// - /// Gets the semantic block arguments. + /// Initializes a new instance of the class as a synthetic block with no corresponding source text. /// - public IReadOnlyList Arguments { get; } = arguments; + /// The semantic reference to the block label. + /// The semantic block arguments. + /// The operations contained in the block. + public Block(BlockReference labelReference, IReadOnlyList arguments, IReadOnlyList operations) + { + Syntax = null; + LabelReference = labelReference; + Arguments = arguments; + Operations = operations; + } /// - /// Gets the operations contained in the block. + /// Gets the concrete syntax node for the block, or null if this is a synthetic block with no corresponding source text. /// - public IReadOnlyList Operations { get; } = operations; + public BlockSyntax? Syntax { get; } /// - /// Gets the block label, including the leading ^. + /// Gets the semantic reference to the block label. + /// + public BlockReference LabelReference { get; } + + /// + /// Gets the semantic block arguments. + /// + public IReadOnlyList Arguments { get; } + + /// + /// Gets the operations contained in the block. /// - public string Label => Syntax.Label; + public IReadOnlyList Operations { get; } /// - /// Gets the typed reference to the block label. + /// Gets the block label, including the leading ^. /// - public BlockReference LabelReference { get; } = new BlockReference(syntax.LabelToken); + public string Label => LabelReference.Label; /// /// Gets the source location of the block label, if known. diff --git a/src/MLIR/Semantics/BlockReference.cs b/src/MLIR/Semantics/BlockReference.cs index 62af382..9761f6d 100644 --- a/src/MLIR/Semantics/BlockReference.cs +++ b/src/MLIR/Semantics/BlockReference.cs @@ -18,9 +18,9 @@ public BlockReference(SyntaxToken token) } /// - /// Initializes a new instance of the struct from a label text. + /// Initializes a new instance of the struct for a synthetic reference with no corresponding source token. /// - /// The block label text. + /// The block label text, including the leading ^. public BlockReference(string label) { Token = null; @@ -28,7 +28,7 @@ public BlockReference(string label) } /// - /// Gets the syntax token for the block label. + /// Gets the syntax token for the block label, or null if this is a synthetic reference with no corresponding source token. /// public SyntaxToken? Token { get; } diff --git a/src/MLIR/Semantics/Operation.cs b/src/MLIR/Semantics/Operation.cs index d96eb23..22c07ff 100644 --- a/src/MLIR/Semantics/Operation.cs +++ b/src/MLIR/Semantics/Operation.cs @@ -2,7 +2,6 @@ namespace MLIR.Semantics; using System; using System.Collections.Generic; -using MLIR.Construction; using MLIR.Dialects; using MLIR.Syntax; @@ -15,7 +14,7 @@ public abstract class Operation /// Initializes a new instance of the class. /// protected Operation( - OperationSyntax syntax, + OperationSyntax? syntax, string name, OperationDefinition? definition) { @@ -25,9 +24,9 @@ protected Operation( } /// - /// Gets the concrete syntax node for the operation. + /// Gets the concrete syntax node for the operation, or null if this is a synthetic operation with no corresponding source text. /// - public OperationSyntax Syntax { get; } + public OperationSyntax? Syntax { get; } /// /// Gets the canonical operation name without MLIR string-literal quoting. @@ -75,9 +74,9 @@ protected Operation( public bool IsKnown => Definition != null; /// - /// Gets the operation name exactly as written in the source. + /// Gets the operation name exactly as written in the source, or null if this is a synthetic operation with no corresponding source text. /// - public string SyntaxName => Syntax.Name; + public string? SyntaxName => Syntax?.Name; /// /// Gets the dialect namespace portion of the operation name, if present. @@ -109,7 +108,7 @@ public string DialectName /// /// Gets the source location of the operation name, if known. /// - public SourceLocation Location => SourceLocation.FromToken(Syntax.NameToken); + public SourceLocation Location => Syntax != null ? SourceLocation.FromToken(Syntax.NameToken) : SourceLocation.Unknown; /// /// Determines whether the operation has an attribute with the supplied name. diff --git a/src/MLIR/Semantics/Region.cs b/src/MLIR/Semantics/Region.cs index 1406599..c2c574d 100644 --- a/src/MLIR/Semantics/Region.cs +++ b/src/MLIR/Semantics/Region.cs @@ -9,14 +9,14 @@ namespace MLIR.Semantics; /// /// Initializes a new instance of the class. /// -/// The concrete syntax node for the region. +/// The concrete syntax node for the region, or null for a synthetic region with no corresponding source text. /// The semantic blocks contained in the region. -public sealed class Region(RegionSyntax syntax, IReadOnlyList blocks) +public sealed class Region(RegionSyntax? syntax, IReadOnlyList blocks) { /// - /// Gets the concrete syntax node for the region. + /// Gets the concrete syntax node for the region, or null if this is a synthetic region with no corresponding source text. /// - public RegionSyntax Syntax { get; } = syntax; + public RegionSyntax? Syntax { get; } = syntax; /// /// Gets the semantic blocks contained in the region. diff --git a/src/MLIR/Semantics/UnknownAttributeValue.cs b/src/MLIR/Semantics/UnknownAttributeValue.cs index f066907..fcfa498 100644 --- a/src/MLIR/Semantics/UnknownAttributeValue.cs +++ b/src/MLIR/Semantics/UnknownAttributeValue.cs @@ -11,7 +11,7 @@ public sealed class UnknownAttributeValue : AttributeValue /// /// Initializes a new instance of the class. /// - public UnknownAttributeValue(RawSyntaxText syntax, string? name, AttributeDefinition? definition, SourceLocation location) + public UnknownAttributeValue(AttributeValueSyntax syntax, string? name, AttributeDefinition? definition, SourceLocation location) : base(syntax, name, definition, location) { } diff --git a/src/MLIR/Semantics/ValueReference.cs b/src/MLIR/Semantics/ValueReference.cs index 708b71b..f462e83 100644 --- a/src/MLIR/Semantics/ValueReference.cs +++ b/src/MLIR/Semantics/ValueReference.cs @@ -18,7 +18,7 @@ public ValueReference(SyntaxToken token) } /// - /// Initializes a new instance of the struct from a value name. + /// Initializes a new instance of the struct for a synthetic value with no corresponding source token. /// /// The SSA value name. public ValueReference(string name) @@ -28,7 +28,7 @@ public ValueReference(string name) } /// - /// Gets the syntax token for the SSA value. + /// Gets the syntax token for the SSA value, or null if this is a synthetic reference with no corresponding source token. /// public SyntaxToken? Token { get; } diff --git a/src/MLIR/Transforms/AssemblySyntaxBuilder.cs b/src/MLIR/Transforms/AssemblySyntaxBuilder.cs index c700d83..6ba810c 100644 --- a/src/MLIR/Transforms/AssemblySyntaxBuilder.cs +++ b/src/MLIR/Transforms/AssemblySyntaxBuilder.cs @@ -1,5 +1,6 @@ namespace MLIR.Transforms; +using System; using System.Collections.Generic; using MLIR.Construction; using MLIR.Semantics; @@ -45,11 +46,36 @@ public OperationSyntax WithBody(Operation operation, OperationBodySyntax body) public OperationSyntax RewriteOperation(Operation operation, OperationBodySyntax body, SyntaxToken? nameToken = null) { + if (operation.Syntax != null) + { + return new OperationSyntax( + operation.Syntax.ResultTokens, + operation.Syntax.ResultCommaTokens, + operation.Syntax.EqualsToken, + nameToken ?? operation.Syntax.NameToken, + body); + } + + // Synthesize tokens for a synthetic operation with no source syntax. + var results = operation.Results; + var resultTokens = new List(results.Count); + foreach (var result in results) + { + resultTokens.Add(new SyntaxToken(result)); + } + + var resultCommaTokens = new List(Math.Max(0, results.Count - 1)); + for (var i = 1; i < results.Count; i++) + { + resultCommaTokens.Add(new SyntaxToken(",")); + } + + var equalsToken = results.Count > 0 ? (SyntaxToken?)new SyntaxToken("=") : null; return new OperationSyntax( - operation.Syntax.ResultTokens, - operation.Syntax.ResultCommaTokens, - operation.Syntax.EqualsToken, - nameToken ?? operation.Syntax.NameToken, + resultTokens, + resultCommaTokens, + equalsToken, + nameToken ?? new SyntaxToken(operation.Name), body); } @@ -84,12 +110,42 @@ private GenericOperationBodySyntax GetGenericBody(Operation operation) operation.Results, operation.Operands, operation.Successors, - operation.Regions.Select(r => r.Syntax).ToList(), - operation.Attributes.Select(a => Factory.Attr(a.Name, a.Value.Syntax.Text)).ToList(), + operation.Regions.Select(BuildRegion).ToList(), + operation.Attributes.Select(BuildNamedAttribute).ToList(), operation.TypeSignatureReference != null ? operation.TypeSignatureReference.Syntax : null ).Body; } + public NamedAttributeSyntax BuildNamedAttribute(NamedAttribute attribute) + { + if (attribute.Syntax != null) + { + return attribute.Syntax; + } + + // Synthesize an attribute syntax for a synthetic attribute with no source syntax. + return new NamedAttributeSyntax( + new SyntaxToken(attribute.Name), + new SyntaxToken("="), + BuildAttributeValue(attribute.Value)); + } + + public AttributeValueSyntax BuildAttributeValue(AttributeValue attributeValue) + { + if (attributeValue.Syntax != null) + { + return attributeValue.Syntax; + } + + if (attributeValue is UnknownAttributeValue unknownAttributeValue) + { + // For unknown attribute values, we want to preserve the original syntax if possible, even if it was not recognized as a valid attribute value. + return unknownAttributeValue.Syntax!; + } + + throw new InvalidOperationException($"Cannot build syntax for unrecognized attribute value of type {attributeValue.GetType().FullName}."); + } + public RegionSyntax BuildRegion(Region region) { var blocks = new List(region.Blocks.Count); @@ -98,7 +154,10 @@ public RegionSyntax BuildRegion(Region region) blocks.Add(BuildBlock(block)); } - return new RegionSyntax(region.Syntax.OpenBraceToken, blocks, region.Syntax.CloseBraceToken); + return new RegionSyntax( + region.Syntax?.OpenBraceToken ?? new SyntaxToken("{"), + blocks, + region.Syntax?.CloseBraceToken ?? new SyntaxToken("}")); } public BlockSyntax BuildBlock(Block block) @@ -109,11 +168,19 @@ public BlockSyntax BuildBlock(Block block) operations.Add(BuildOperation(operation)); } - return new BlockSyntax( - block.Syntax.LabelToken, - block.Syntax.Arguments, - block.Syntax.ColonToken, - operations); + if (block.Syntax != null) + { + return new BlockSyntax( + block.Syntax.LabelToken, + block.Syntax.Arguments, + block.Syntax.ColonToken, + operations); + } + + // Synthesize a block syntax for a synthetic block with no source syntax. + // Use "^entry" as the fallback label: the parser uses this synthetic label for implicit + // entry blocks, and BlockSyntax.WriteTo omits it during printing when there are no arguments. + return new BlockSyntax(block.Label, [], operations); } } } diff --git a/tests/MLIR.Tests/SemanticTests.cs b/tests/MLIR.Tests/SemanticTests.cs index 69790ff..2ff3449 100644 --- a/tests/MLIR.Tests/SemanticTests.cs +++ b/tests/MLIR.Tests/SemanticTests.cs @@ -175,6 +175,29 @@ public GeneratedAddIOperation(OperationConstructionContext context) public ValueReference ResultValue { get; } } + private sealed class SyntheticOperation : Operation + { + public SyntheticOperation(string name) + : base(null, name, null) + { + } + + public override IReadOnlyList Regions => []; + public override IReadOnlyList Attributes => []; + public override TypeReference? TypeSignatureReference => null; + public override IReadOnlyList ResultValues => []; + public override IReadOnlyList OperandValues => []; + public override IReadOnlyList SuccessorReferences => []; + } + + private sealed class SyntheticAttributeValue : AttributeValue + { + public SyntheticAttributeValue(string name) + : base(null, name, null, SourceLocation.Unknown) + { + } + } + private sealed class PrefixConstantAssemblyFormat : IOperationAssemblyFormat { public bool TryParse( @@ -214,12 +237,13 @@ public Operation Bind(OperationSyntax syntax, OperationDefinition definition, Bi public OperationSyntax Rewrite(Operation operation, OperationSyntaxTransformContext context) { var genericBody = context.TransformGenericBody(operation); + var valueAttr = operation.Attributes.FirstOrDefault(a => a.Name == "value"); var body = new PrefixConstantBodySyntax( - operation.HasAttribute("value") ? operation.GetAttribute("value").Value.Syntax : new RawSyntaxText(string.Empty), + valueAttr != null && valueAttr.Value.Syntax != null ? valueAttr.Value.Syntax.GetRawText() : new RawSyntaxText(string.Empty), genericBody.TypeSignatureColonToken ?? new SyntaxToken(":"), genericBody.TypeSignatureSyntax ?? throw new InvalidOperationException("Expected a type signature in the generic body for rewriting."), genericBody.Attributes); - var sourceNameToken = operation.Syntax.NameToken; + var sourceNameToken = operation.Syntax!.NameToken; var rewrittenNameToken = new SyntaxToken(operation.Name, sourceNameToken.LeadingTrivia, sourceNameToken.Line, sourceNameToken.Column); return context.RewriteOperation(operation, body, rewrittenNameToken); } @@ -234,7 +258,7 @@ public void Bind(AttributeValue attribute, AttributeAssemblyBindingContext conte denseAttribute.BindDense(); } - if (!attribute.Syntax.Text.Contains("tensor<")) + if (!attribute.Syntax!.GetRawText().Text.Contains("tensor<")) { context.Report("dense attribute literals should mention a tensor type."); } @@ -344,7 +368,7 @@ public void BindsNestedRegionsBlocksArgumentsAndAttributes() Assert.Equal("i32", block.Arguments[0].Type.Text); Assert.Single(nestedOperation.Attributes); Assert.Equal("value", nestedOperation.Attributes[0].Name); - Assert.Equal("1 : i32", nestedOperation.Attributes[0].Value.Syntax.Text); + Assert.Equal("1 : i32", nestedOperation.Attributes[0].Value.Syntax!.GetRawText().Text); Assert.Equal("%arg0", block.Arguments[0].Value.Name); Assert.Equal("i32", block.Arguments[0].TypeReference.Name); } @@ -608,7 +632,7 @@ public void OperationCanRetrieveAttributesByName() var attribute = module.Operations[0].GetAttribute("value"); Assert.Equal("value", attribute.Name); - Assert.Equal("0 : i32", attribute.Value.Syntax.Text); + Assert.Equal("0 : i32", attribute.Value.Syntax!.GetRawText().Text); } [Fact] @@ -625,7 +649,7 @@ public void OperationViewProvidesTypedWrapperOverSemanticOperation() Assert.Equal("%0", view.Results[0]); Assert.Equal("%0", view.ResultValue.Name); - Assert.Equal("0 : i32", view.ValueAttribute.Value.Syntax.Text); + Assert.Equal("0 : i32", view.ValueAttribute.Value.Syntax!.GetRawText().Text); } [Fact] @@ -756,7 +780,7 @@ public void DocumentCanParseRegisteredCustomAssemblyFormats() var module = Binder.BindModule(document.Module, registry); Assert.Equal("%0 = arith.constant 0 : i32", module.ToText()); - Assert.Equal("0", module.Operations[0].GetAttribute("value").Value.Syntax.Text); + Assert.Equal("0", module.Operations[0].GetAttribute("value").Value.Syntax!.GetRawText().Text); } [Fact] @@ -953,4 +977,44 @@ public void RegistryRejectsDuplicateAttributeAndTypeRegistrations() Assert.Contains("already registered", attributeException.Message); Assert.Contains("already registered", typeException.Message); } + + [Fact] + public void SyntheticBlockHasNullSyntaxAndUnknownLocation() + { + var syntheticBlock = new Block(new BlockReference("^entry"), [], []); + + Assert.Null(syntheticBlock.Syntax); + Assert.Equal("^entry", syntheticBlock.Label); + Assert.False(syntheticBlock.Location.IsKnown); + } + + [Fact] + public void SyntheticRegionHasNullSyntax() + { + var syntheticRegion = new Region(null, []); + + Assert.Null(syntheticRegion.Syntax); + } + + [Fact] + public void SyntheticOperationHasNullSyntaxAndUnknownLocation() + { + var syntheticOperation = new SyntheticOperation("test.synthetic"); + + Assert.Null(syntheticOperation.Syntax); + Assert.Null(syntheticOperation.SyntaxName); + Assert.False(syntheticOperation.Location.IsKnown); + Assert.Equal("test.synthetic", syntheticOperation.Name); + Assert.Equal("test", syntheticOperation.DialectName); + } + + [Fact] + public void SyntheticAttributeValueHasNullSyntax() + { + var syntheticAttribute = new SyntheticAttributeValue("test"); + + Assert.Null(syntheticAttribute.Syntax); + Assert.Equal("test", syntheticAttribute.Name); + Assert.False(syntheticAttribute.Location.IsKnown); + } }