Exemple #1
0
        /// <summary>
        /// Applies a binary arithmetic intrinsic to an integer value stored
        /// at an address and a 'one' integer constant.
        /// </summary>
        /// <param name="pointer">
        /// An address that contains an integer variable to update.
        /// </param>
        /// <param name="block">
        /// The block to write the update instructions to.
        /// </param>
        /// <param name="op">
        /// The operator to apply.
        /// </param>
        private static void IncrementOrDecrement(
            ValueTag pointer,
            BasicBlockBuilder block,
            string op)
        {
            var integerType = ((PointerType)block.Graph.GetValueType(pointer)).ElementType;

            block.AppendInstruction(
                Instruction.CreateStore(
                    integerType,
                    pointer,
                    block.AppendInstruction(
                        Instruction.CreateBinaryArithmeticIntrinsic(
                            op,
                            false,
                            integerType,
                            block.AppendInstruction(
                                Instruction.CreateLoad(
                                    integerType,
                                    pointer)),
                            block.AppendInstruction(
                                Instruction.CreateConstant(
                                    new IntegerConstant(1, integerType.GetIntegerSpecOrNull()),
                                    integerType))))));
        }
Exemple #2
0
        private bool TryCompileAssignment(AssignmentSyntax assignment, BasicBlockBuilder builder)
        {
            Debug.Assert(_methodInProgress != null);

            // Get the target variable
            if (!_variableMap.TryGetVariable(assignment.Variable.Name, out var targetIndex))
            {
                _diagnostics.Add(DiagnosticCode.VariableNotFound, assignment.Variable.Position, assignment.Variable.Name);
                return(false);
            }
            var expectedType = _methodInProgress.Values[targetIndex].Type;

            // Compile the expression
            var sourceIndex = ExpressionCompiler.TryCompileExpression(assignment.Value,
                                                                      expectedType, _methodInProgress, builder, this, _diagnostics);

            if (sourceIndex == -1)
            {
                return(false);
            }

            // Emit a copy operation
            builder.AppendInstruction(Opcode.CopyValue, (ushort)sourceIndex, 0, (ushort)targetIndex);
            return(true);
        }
Exemple #3
0
        /// <summary>
        /// Fuses two fusible basic blocks.
        /// </summary>
        /// <param name="head">The 'head' block.</param>
        /// <param name="tail">The 'tail' block.</param>
        private static void FuseBlocks(BasicBlockBuilder head, BasicBlockTag tail)
        {
            var jump = (JumpFlow)head.Flow;

            // Replace branch parameters by their respective arguments.
            var replacements = new Dictionary <ValueTag, ValueTag>();

            foreach (var pair in jump.Branch.ZipArgumentsWithParameters(head.Graph))
            {
                replacements.Add(pair.Key, pair.Value.ValueOrNull);
            }
            head.Graph.ReplaceUses(replacements);

            // Move instructions around.
            var tailBlock = head.Graph.GetBasicBlock(tail);

            foreach (var instruction in tailBlock.NamedInstructions)
            {
                instruction.MoveTo(head);
            }

            // Update the block's flow.
            head.Flow = tailBlock.Flow;

            // Delete the 'tail' block.
            head.Graph.RemoveBasicBlock(tail);
        }
Exemple #4
0
        /// <summary>
        /// Tries to replace a return flow with a branch to
        /// the entry point.
        /// </summary>
        /// <param name="block">
        /// A block that ends in return flow.
        /// </param>
        /// <param name="returnValue">
        /// The value returned by the return flow.
        /// </param>
        /// <param name="graph">
        /// The graph that defines the block.
        /// </param>
        /// <param name="method">
        /// The method that is being optimized.
        /// </param>
        /// <param name="ordering">
        /// An instruction ordering model.
        /// </param>
        /// <returns>
        /// <c>true</c> if the return has been replaced with a branch
        /// to the entry point by this method; otherwise, <c>false</c>.
        /// </returns>
        private bool TryReplaceWithBranchToEntry(
            BasicBlockBuilder block,
            Instruction returnValue,
            FlowGraphBuilder graph,
            IMethod method,
            InstructionOrdering ordering)
        {
            var      insnsToEliminate = new HashSet <ValueTag>();
            ValueTag callTag          = null;

            // Jump through copy instructions.
            while (returnValue.Prototype is CopyPrototype)
            {
                callTag = ((CopyPrototype)returnValue.Prototype).GetCopiedValue(returnValue);
                insnsToEliminate.Add(callTag);
                if (!graph.ContainsInstruction(callTag))
                {
                    // We encountered a basic block parameter, which we can't really
                    // peek through. Abort.
                    return(false);
                }
                returnValue = graph.GetInstruction(callTag).Instruction;
            }

            if ((callTag == null || graph.GetValueParent(callTag).Tag == block.Tag) &&
                IsSelfCallPrototype(returnValue.Prototype, method))
            {
                // Now all we have to do is make sure that there is no instruction
                // that must run after the call.
                if (callTag != null)
                {
                    var selection = graph.GetInstruction(callTag).NextInstructionOrNull;
                    while (selection != null)
                    {
                        if (ordering.MustRunBefore(callTag, selection))
                        {
                            // Aw, snap. Looks like we can't reorder the call, which
                            // means no tail call elimination. Abort.
                            return(false);
                        }
                        selection = selection.NextInstructionOrNull;
                    }
                }

                // Remove some instructions.
                foreach (var tag in insnsToEliminate)
                {
                    graph.RemoveInstruction(tag);
                }

                // Turn the return flow into a jump.
                block.Flow = new JumpFlow(graph.EntryPointTag, returnValue.Arguments);
                return(true);
            }
            else
            {
                return(false);
            }
        }
        public static ControlFlowGraph CreateGraph(BoundBlock block, FunctionSymbol function = null)
        {
            var blockBuilder = new BasicBlockBuilder();
            var edgeBuilder  = new GraphBuilder(function);
            var blocks       = blockBuilder.Build(block);

            return(edgeBuilder.Build(blocks));
        }
            /// <summary>
            /// Fills the given block.
            /// </summary>
            private void FillBlock(BasicBlockBuilder block)
            {
                if (!processedBlocks.Add(block.Tag))
                {
                    // Block is already being processed.
                    return;
                }

                // Try to seal the block right away if possible.
                if (CanSealBlock(block.Tag))
                {
                    SealBlock(block.Tag);
                }

                // Actually fill the block, starting with the block's instructions.
                foreach (var selection in block.NamedInstructions)
                {
                    Instruction newInstruction;
                    if (RewriteInstruction(selection.Instruction, block, out newInstruction))
                    {
                        selection.Instruction = newInstruction;
                    }
                }

                // Also process instructions in the block's flow.
                var  newFlowInstructions = new List <Instruction>();
                bool flowChanged         = false;

                foreach (var instruction in block.Flow.Instructions)
                {
                    Instruction newInstruction;
                    if (RewriteInstruction(instruction, block, out newInstruction))
                    {
                        flowChanged = true;
                        newFlowInstructions.Add(newInstruction);
                    }
                    else
                    {
                        newFlowInstructions.Add(instruction);
                    }
                }
                if (flowChanged)
                {
                    block.Flow = block.Flow.WithInstructions(newFlowInstructions);
                }

                // Declare the block to be filled.
                filledBlocks.Add(block.Tag);

                // Seal this block.
                SealAllSealableBlocks();

                // Fill successor blocks.
                foreach (var target in block.Flow.BranchTargets)
                {
                    FillBlock(graphBuilder.GetBasicBlock(target));
                }
            }
Exemple #7
0
 public ConditionalAccessOperationTracker(
     ArrayBuilder <IOperation> operations,
     BasicBlockBuilder whenNull
     )
 {
     Debug.Assert(operations != null && whenNull != null);
     Operations = operations;
     WhenNull   = whenNull;
 }
Exemple #8
0
        public static ControlFlowGraph Create(BoundBlockStatement body)
        {
            var basicBlockBuilder = new BasicBlockBuilder();
            var blocks            = basicBlockBuilder.Build(body);

            var graphBuilder = new GraphBuilder();

            return(graphBuilder.Build(blocks));
        }
 public void Free()
 {
     Ordinal = -1;
     _statements?.Free();
     _statements = null;
     _predecessors?.Free();
     _predecessors = null;
     _predecessor1 = null;
     _predecessor2 = null;
 }
Exemple #10
0
 /// <inheritdoc/>
 public override InstructionBuilder GetInstructionBuilder(BasicBlockBuilder block, int instructionIndex)
 {
     if (instructionIndex == 0)
     {
         return(new TryFlowInstructionBuilder(block));
     }
     else
     {
         throw new IndexOutOfRangeException();
     }
 }
Exemple #11
0
        /// <summary>
        /// Replaces a block's flow with an unconditional jump to an exception
        /// branch. Replaces 'try' flow exception arguments in that exception
        /// branch with a captured exception value.
        /// </summary>
        /// <param name="block">The block to rewrite.</param>
        /// <param name="exceptionBranch">The exception branch to jump to unconditionally.</param>
        /// <param name="capturedException">
        /// The captured exception to pass instead of a 'try' flow exception argument.
        /// </param>
        private static void JumpToExceptionBranch(
            BasicBlockBuilder block,
            Branch exceptionBranch,
            ValueTag capturedException)
        {
            var branch = exceptionBranch.WithArguments(
                exceptionBranch.Arguments
                .Select(arg => arg.IsTryException ? BranchArgument.FromValue(capturedException) : arg)
                .ToArray());

            block.Flow = new JumpFlow(branch);
        }
 /// <summary>
 /// Creates a CIL analysis context.
 /// </summary>
 /// <param name="block">
 /// The initial basic block to fill.
 /// </param>
 /// <param name="analyzer">
 /// The method body analyzer to which this
 /// analysis context belongs.
 /// </param>
 /// <param name="exceptionHandlers">
 /// The exception handlers for the CIL analysis context.
 /// </param>
 public CilAnalysisContext(
     BasicBlockBuilder block,
     ClrMethodBodyAnalyzer analyzer,
     IReadOnlyList <CilExceptionHandler> exceptionHandlers)
 {
     this.Block             = block;
     this.Analyzer          = analyzer;
     this.ExceptionHandlers = exceptionHandlers;
     this.stack             = new Stack <ValueTag>(
         block.Parameters.Select(param => param.Tag));
     this.IsTerminated = false;
 }
Exemple #13
0
        private bool TryCompileVariableDeclaration(VariableDeclarationSyntax declaration,
                                                   BasicBlockBuilder builder)
        {
            Debug.Assert(_methodInProgress != null);
            var localCount = _methodInProgress.Values.Count;

            // Resolve the type and verify that it is not void
            if (!TypeResolver.TryResolve(declaration.Type, _diagnostics, out var type))
            {
                return(false);
            }
            Debug.Assert(type != null);
            if (type.Equals(SimpleType.Void))
            {
                _diagnostics.Add(DiagnosticCode.VoidIsNotValidType, declaration.Position, declaration.Name);
                return(false);
            }

            // Compile the initial value (either a runtime expression or compile-time constant)
            var localIndex = ExpressionCompiler.TryCompileExpression(declaration.InitialValueExpression,
                                                                     type, _methodInProgress, builder, this, _diagnostics);

            if (localIndex == -1)
            {
                return(false);
            }

            // There are two cases:
            //   - ExpressionCompiler created temporaries, the last of which is our initialized variable,
            //   - ExpressionCompiler returned a reference to an existent local ("Type value = anotherVariable" only).
            // In the latter case, we must create a new local and emit a copy.
            if (localCount == _methodInProgress.Values.Count)
            {
                var source = localIndex;

                // Don't bother figuring out a correct initial value, it won't be used anyways
                localIndex = _methodInProgress.AddLocal(type, LocalFlags.None);
                builder.AppendInstruction(Opcode.CopyValue, (ushort)source, 0, (ushort)localIndex);
            }

            // Add it to the variable map (unless the name is already in use)
            if (!_variableMap.TryAddVariable(declaration.Name, localIndex))
            {
                _diagnostics.Add(DiagnosticCode.VariableAlreadyDefined, declaration.Position, declaration.Name);
                return(false);
            }

            return(true);
        }
 public void MoveStatementsFrom(BasicBlockBuilder other)
 {
     if (other._statements == null)
     {
         return;
     }
     else if (_statements == null)
     {
         _statements       = other._statements;
         other._statements = null;
     }
     else
     {
         _statements.AddRange(other._statements);
         other._statements.Clear();
     }
 }
            public void RemovePredecessor(BasicBlockBuilder predecessor)
            {
                Debug.Assert(predecessor != null);

                if (_predecessors != null)
                {
                    Debug.Assert(_predecessor1 == null);
                    Debug.Assert(_predecessor2 == null);
                    _predecessors.Remove(predecessor);
                }
                else if (_predecessor1 == predecessor)
                {
                    _predecessor1 = null;
                }
                else if (_predecessor2 == predecessor)
                {
                    _predecessor2 = null;
                }
            }
Exemple #16
0
        private static void ParseInstruction(string line, CompiledMethod method, BasicBlockBuilder builder,
                                             Dictionary <string, int> calledMethodIndices)
        {
            var lineParts = line.Split(' ', StringSplitOptions.RemoveEmptyEntries);

            Assert.That(Enum.TryParse <Opcode>(lineParts[0], out var opcode), Is.True, $"Unknown opcode: {lineParts[0]}");

            if (opcode == Opcode.Return)
            {
                // Remove leading # before parsing the value number
                var sourceIndex = ushort.Parse(lineParts[1].AsSpan(1));

                builder.AppendInstruction(Opcode.Return, sourceIndex, 0, 0);
            }
            else if (opcode == Opcode.BranchIf)
            {
                var sourceIndex      = ushort.Parse(lineParts[1].AsSpan(1));
                var targetBlockIndex = int.Parse(lineParts[3].AsSpan(3));

                builder.AppendInstruction(Opcode.BranchIf, sourceIndex, 0, 0);
                builder.SetAlternativeSuccessor(targetBlockIndex);
            }
            else if (opcode == Opcode.Load)
            {
                var value     = ResolveValue(lineParts[1]);
                var destIndex = ushort.Parse(lineParts[3].AsSpan(1));

                builder.AppendInstruction(Opcode.Load, value, 0, destIndex);
            }
            else if (opcode == Opcode.Call)
            {
                // Emulate function indices by creating them sequentially upwards from 100
                // If a function is called twice, it will have the same index both times
                var functionName = lineParts[1].Substring(0, lineParts[1].IndexOf('('));
                if (!calledMethodIndices.TryGetValue(functionName, out var functionIndex))
                {
                    functionIndex = 100 + calledMethodIndices.Count;
                    calledMethodIndices.Add(functionName, functionIndex);
                }

                var destIndex      = ushort.Parse(lineParts[^ 1].AsSpan(1));
 /// <summary>
 /// Tries to simplify a basic block's switch flow, provided that the
 /// block ends in switch flow.
 /// </summary>
 /// <param name="block">The basic block to simplify.</param>
 /// <returns>
 /// <c>true</c> if the block's flow was simplified; otherwise, <c>false</c>.
 /// </returns>
 public static bool TrySimplifySwitchFlow(BasicBlockBuilder block)
 {
     if (block.Flow is SwitchFlow)
     {
         var flow       = (SwitchFlow)block.Flow;
         var simpleFlow = SimplifySwitchFlow(flow, block.Graph.ImmutableGraph);
         if (flow != simpleFlow)
         {
             if (simpleFlow is JumpFlow)
             {
                 // If a switch flow gets simplified to a jump
                 // flow, then we should still ensure that the
                 // switch value is evaluated.
                 block.AppendInstruction(flow.SwitchValue);
             }
             block.Flow = simpleFlow;
             return(true);
         }
     }
     return(false);
 }
Exemple #18
0
            /// <inheritdoc/>
            public override BlockFlow Emit(FlowGraphBuilder graph, ValueTag value)
            {
                int caseCounter = 0;
                BasicBlockBuilder currentBlock = null;
                var       defaultJump          = new JumpFlow(flow.DefaultBranch);
                BlockFlow result = defaultJump;

                foreach (var switchCase in flow.Cases)
                {
                    foreach (var pattern in switchCase.Values)
                    {
                        caseCounter++;
                        var nextBlock = graph.AddBasicBlock("case" + caseCounter);
                        var blockFlow = new SwitchFlow(
                            Instruction.CreateCopy(graph.GetValueType(value), value),
                            new[]
                        {
                            new SwitchCase(ImmutableHashSet.Create(pattern), switchCase.Branch)
                        },
                            new Branch(nextBlock));

                        if (currentBlock == null)
                        {
                            result = blockFlow;
                        }
                        else
                        {
                            currentBlock.Flow = blockFlow;
                        }
                        currentBlock = nextBlock;
                    }
                }

                if (currentBlock != null)
                {
                    currentBlock.Flow = defaultJump;
                }

                return(result);
            }
Exemple #19
0
        /// <summary>
        /// Emits instructions that write a character to the output stream.
        /// </summary>
        /// <param name="block">A basic block builder.</param>
        /// <param name="character">The character to write to the output stream.</param>
        public void EmitWrite(ref BasicBlockBuilder block, ValueTag character)
        {
            if (WriteMethod == null)
            {
                // Do nothing if the 'write' method is null.
                return;
            }

            // Convert the character to a type that the 'write' method is okay with.
            var convChar = block.AppendInstruction(
                Instruction.CreateConvertIntrinsic(
                    WriteMethod.Parameters[0].Type,
                    block.Graph.GetValueType(character),
                    character));

            // Call the 'write' method.
            block.AppendInstruction(
                Instruction.CreateCall(
                    WriteMethod,
                    MethodLookup.Static,
                    new ValueTag[] { convChar }));
        }
Exemple #20
0
        private static ValueTag GetDataPointer(
            IType elementType,
            ValueTag cellArray,
            ValueTag cellIndexAlloca,
            BasicBlockBuilder block)
        {
            var arrayType      = block.Graph.GetValueType(cellArray);
            var indexAllocType = block.Graph.GetValueType(cellIndexAlloca);
            var indexType      = ((PointerType)indexAllocType).ElementType;

            return(block.AppendInstruction(
                       Instruction.CreateGetElementPointerIntrinsic(
                           elementType,
                           arrayType,
                           new[] { indexType },
                           cellArray,
                           new ValueTag[]
            {
                block.AppendInstruction(
                    Instruction.CreateLoad(indexType, cellIndexAlloca))
            })));
        }
            public void AddPredecessor(BasicBlockBuilder predecessor)
            {
                Debug.Assert(predecessor != null);

                if (_predecessors != null)
                {
                    Debug.Assert(_predecessor1 == null);
                    Debug.Assert(_predecessor2 == null);
                    _predecessors.Add(predecessor);
                }
                else if (_predecessor1 == predecessor)
                {
                    return;
                }
                else if (_predecessor2 == predecessor)
                {
                    return;
                }
                else if (_predecessor1 == null)
                {
                    _predecessor1 = predecessor;
                }
                else if (_predecessor2 == null)
                {
                    _predecessor2 = predecessor;
                }
                else
                {
                    _predecessors = PooledHashSet <BasicBlockBuilder> .GetInstance();

                    _predecessors.Add(_predecessor1);
                    _predecessors.Add(_predecessor2);
                    _predecessors.Add(predecessor);
                    Debug.Assert(_predecessors.Count == 3);
                    _predecessor1 = null;
                    _predecessor2 = null;
                }
            }
Exemple #22
0
        // TODO: Benchmark whether it would be beneficial to pass the unchanging parameters to the private methods
        //       in a readonly struct. We pass a lot of parameters and the call trees run quite deep.

        /// <summary>
        /// Compiles the given expression syntax tree and verifies its type.
        /// Returns the local index if successful, returns -1 and logs diagnostics if the compilation fails.
        /// </summary>
        /// <param name="syntax">The root of the expression syntax tree.</param>
        /// <param name="expectedType">The expected type of the evaluated expression. If null, the type is not checked.</param>
        /// <param name="method">The method to store and get local values from.</param>
        /// <param name="builder">Intermediate representation builder.</param>
        /// <param name="nameResolver">The resolver for variable and method names.</param>
        /// <param name="diagnostics">Receiver for possible diagnostics.</param>
        public static int TryCompileExpression(
            ExpressionSyntax syntax,
            TypeDefinition?expectedType,
            CompiledMethod method,
            BasicBlockBuilder builder,
            INameResolver nameResolver,
            IDiagnosticSink diagnostics)
        {
            // Compile the expression
            if (!InternalTryCompileExpression(syntax, method, builder, nameResolver, diagnostics, out var value))
            {
                return(-1);
            }

            // Verify the type
            // TODO: Once multiple integer types exist, there must be some notion of conversions
            if (expectedType != null && !value.Type.Equals(expectedType))
            {
                diagnostics.Add(DiagnosticCode.TypeMismatch, syntax.Position,
                                value.Type.TypeName, expectedType.TypeName);
                return(-1);
            }

            // Also, if the expression is constant, verify that it is representable
            if (!value.LocalIndex.HasValue && value.Type.Equals(SimpleType.Int32))
            {
                if (value.ConstantValue.AsSignedInteger < int.MinValue ||
                    value.ConstantValue.AsSignedInteger > int.MaxValue)
                {
                    diagnostics.Add(DiagnosticCode.IntegerConstantOutOfBounds, syntax.Position,
                                    value.ConstantValue.ToString(), SimpleType.Int32.TypeName);
                    return(-1);
                }
            }

            // Store the value in a local
            return(EnsureValueIsStored(value, method, builder));
        }
Exemple #23
0
        private bool TryCompileWhile(WhileStatementSyntax whileSyntax, BasicBlockBuilder builder,
                                     out BasicBlockBuilder newBuilder)
        {
            newBuilder = builder; // Default failure case
            Debug.Assert(_methodInProgress != null);

            // Create a new basic block which will be the backwards branch target
            var conditionBuilder    = builder.CreateSuccessorBlock();
            var conditionBlockIndex = conditionBuilder.Index;

            // Compile the condition
            var conditionValue = ExpressionCompiler.TryCompileExpression(whileSyntax.ConditionSyntax,
                                                                         SimpleType.Bool, _methodInProgress, conditionBuilder, this, _diagnostics);

            if (conditionValue == -1)
            {
                return(false);
            }

            // Then compile the body.
            // The return guarantee is not propagated up as we don't know whether the loop will ever be entered.
            // TODO: Recognizing compile-time constant condition
            var bodyBuilder = conditionBuilder.CreateBranch((ushort)conditionValue);

            if (!TryCompileBlock(whileSyntax.BodySyntax, bodyBuilder, out bodyBuilder, out var _))
            {
                return(false);
            }

            // Create the backwards branch
            bodyBuilder.SetSuccessor(conditionBlockIndex);

            // Create the exit branch
            newBuilder = conditionBuilder.CreateSuccessorBlock();

            return(true);
        }
Exemple #24
0
        private bool TryCompileReturn(ReturnStatementSyntax syntax, BasicBlockBuilder builder)
        {
            Debug.Assert(_methodInProgress != null);
            Debug.Assert(_declaration != null);

            var returnValueNumber = -1; // ExpressionCompiler returns -1 on failure

            if (syntax.ResultExpression is null)
            {
                // Void return: verify that the method really returns void, then add a void local to return
                if (_declaration.ReturnType.Equals(SimpleType.Void))
                {
                    returnValueNumber = _methodInProgress.AddLocal(SimpleType.Void, LocalFlags.None);
                }
                else
                {
                    _diagnostics.Add(DiagnosticCode.TypeMismatch, syntax.Position,
                                     SimpleType.Void.TypeName, _declaration.ReturnType.TypeName);
                }
            }
            else
            {
                // Non-void return: parse the expression, verifying the type
                returnValueNumber = ExpressionCompiler.TryCompileExpression(syntax.ResultExpression,
                                                                            _declaration.ReturnType, _methodInProgress, builder, this, _diagnostics);
            }

            // At this point, a diagnostic should already be logged
            if (returnValueNumber == -1)
            {
                return(false);
            }

            builder.AppendInstruction(Opcode.Return, (ushort)returnValueNumber, 0, 0);
            return(true);
        }
            public ImmutableArray <ControlFlowBranch> ConvertPredecessorsToBranches(ArrayBuilder <BasicBlock> blocks)
            {
                if (!HasPredecessors)
                {
                    _predecessors?.Free();
                    _predecessors = null;
                    return(ImmutableArray <ControlFlowBranch> .Empty);
                }

                BasicBlock block = blocks[Ordinal];

                var branches = ArrayBuilder <ControlFlowBranch> .GetInstance(_predecessors?.Count ?? 2);

                if (_predecessors != null)
                {
                    Debug.Assert(_predecessor1 == null);
                    Debug.Assert(_predecessor2 == null);

                    foreach (BasicBlockBuilder predecessorBlockBuilder in _predecessors)
                    {
                        addBranches(predecessorBlockBuilder);
                    }

                    _predecessors.Free();
                    _predecessors = null;
                }
                else
                {
                    if (_predecessor1 != null)
                    {
                        addBranches(_predecessor1);
                        _predecessor1 = null;
                    }

                    if (_predecessor2 != null)
                    {
                        addBranches(_predecessor2);
                        _predecessor2 = null;
                    }
                }

                // Order predecessors by source ordinal and conditional first to ensure deterministic predecessor ordering.
                branches.Sort((x, y) =>
                {
                    int result = x.Source.Ordinal - y.Source.Ordinal;
                    if (result == 0 && x.IsConditionalSuccessor != y.IsConditionalSuccessor)
                    {
                        if (x.IsConditionalSuccessor)
                        {
                            result = -1;
                        }
                        else
                        {
                            result = 1;
                        }
                    }

                    return(result);
                });

                return(branches.ToImmutableAndFree());

                void addBranches(BasicBlockBuilder predecessorBlockBuilder)
                {
                    BasicBlock predecessor = blocks[predecessorBlockBuilder.Ordinal];

                    if (predecessor.FallThroughSuccessor.Destination == block)
                    {
                        branches.Add(predecessor.FallThroughSuccessor);
                    }

                    if (predecessor.ConditionalSuccessor?.Destination == block)
                    {
                        branches.Add(predecessor.ConditionalSuccessor);
                    }
                }
            }
Exemple #26
0
        /// <summary>
        /// Generates code that runs the given type's static constructors.
        /// </summary>
        /// <param name="BasicBlock">The basic block to extend.</param>
        /// <param name="Type">The type whose static constructors are to be run.</param>
        /// <returns>A basic block builder.</returns>
        public BasicBlockBuilder EmitRunStaticConstructors(BasicBlockBuilder BasicBlock, LLVMType Type)
        {
            if (Type.StaticConstructors.Count == 0)
            {
                return(BasicBlock);
            }

            // Get or create the lock triple for the type. A lock triple consists of:
            //
            //     * A global Boolean flag that tells if all static constructors for
            //       the type have been run.
            //
            //     * A global Boolean flag that tells if the static constructors for
            //       the type are in the process of being run.
            //
            //     * A thread-local Boolean flag that tells if the static constructors
            //       for the type are in the process of being run **by the current
            //       thread.**
            //
            //     * A common metadata node per thread-local variable that is used to
            //       group loads/stores to that variable in an '!invariant.group'.
            //
            Tuple <LLVMValueRef, LLVMValueRef, LLVMValueRef, LLVMValueRef> lockQuad;

            if (!staticConstructorLocks.TryGetValue(Type, out lockQuad))
            {
                string typeName = Type.Namespace.Assembly.Abi.Mangler.Mangle(Type, true);

                var hasRunGlobal = AddGlobal(module, Int8Type(), typeName + "__has_run_cctor");
                hasRunGlobal.SetInitializer(ConstInt(Int8Type(), 0, false));
                hasRunGlobal.SetLinkage(LLVMLinkage.LLVMInternalLinkage);

                var isRunningGlobal = AddGlobal(module, Int8Type(), typeName + "__is_running_cctor");
                isRunningGlobal.SetInitializer(ConstInt(Int8Type(), 0, false));
                isRunningGlobal.SetLinkage(LLVMLinkage.LLVMInternalLinkage);

                var isRunningHereGlobal = AddGlobal(module, Int1Type(), typeName + "__is_running_cctor_here");
                isRunningHereGlobal.SetInitializer(ConstInt(Int1Type(), 0, false));
                isRunningHereGlobal.SetLinkage(LLVMLinkage.LLVMInternalLinkage);
                isRunningHereGlobal.SetThreadLocal(true);

                var mdNode = MDNode(new LLVMValueRef[] { MDString(typeName + "__is_running_cctor_here") });

                lockQuad = new Tuple <LLVMValueRef, LLVMValueRef, LLVMValueRef, LLVMValueRef>(
                    hasRunGlobal, isRunningGlobal, isRunningHereGlobal, mdNode);
                staticConstructorLocks[Type] = lockQuad;
            }

            // A type's static constructors need to be run *before* any of its static fields
            // are accessed. Also, we need to ensure that a type's static constructors are
            // run only *once* in both single-threaded and multi-threaded environments.
            //
            // We'll generate something along the lines of this when a type's static
            // constructors need to be run:
            //
            //     if (!is_running_cctor_here && !has_run_cctor)
            //     {
            //         while (!Interlocked.CompareExchange(ref is_running_cctor, false, true)) { }
            //         if (!has_run_cctor)
            //         {
            //             cctor();
            //             has_run_cctor = true;
            //         }
            //         is_running_cctors = false;
            //     }
            //
            // Or, using gotos:
            //
            //         if (is_running_cctors_here) goto after_cctor;
            //         else goto check_has_run_cctor;
            //
            //     check_has_run_cctor:
            //         is_running_cctors_here = true;
            //         if (has_run_cctor) goto after_cctor;
            //         else goto wait_for_cctor_lock;
            //
            //     wait_for_cctor_lock:
            //         has_exchanged = Interlocked.CompareExchange(ref is_running_cctos, false, true);
            //         if (has_exchanged) goto maybe_run_cctor;
            //         else goto wait_for_cctor_lock;
            //
            //     maybe_run_cctor:
            //         if (has_run_cctor) goto reset_lock;
            //         else goto run_cctor;
            //
            //     run_cctor:
            //         cctor();
            //         has_run_cctor = true;
            //         goto reset_lock;
            //
            //     reset_lock:
            //         is_running_cctors = false;
            //         goto after_cctor;
            //
            //     after_cctor:
            //         ...

            var hasRunPtr          = lockQuad.Item1;
            var isRunningPtr       = lockQuad.Item2;
            var isRunningHerePtr   = lockQuad.Item3;
            var invariantGroup     = lockQuad.Item4;
            var invariantGroupMdId = GetMDKindID("invariant.group");

            var checkHasRunBlock = BasicBlock.CreateChildBlock("check_has_run_cctor");
            var waitForLockBlock = BasicBlock.CreateChildBlock("wait_for_cctor_lock");
            var maybeRunBlock    = BasicBlock.CreateChildBlock("maybe_run_cctor");
            var runBlock         = BasicBlock.CreateChildBlock("run_cctor");
            var resetLockBlock   = BasicBlock.CreateChildBlock("reset_lock");
            var afterBlock       = BasicBlock.CreateChildBlock("after_cctor");

            // Implement the entry basic block.
            BuildCondBr(
                BasicBlock.Builder,
                WithMetadata(
                    BuildLoad(BasicBlock.Builder, isRunningHerePtr, "is_running_cctor_here"),
                    invariantGroupMdId,
                    invariantGroup),
                afterBlock.Block,
                checkHasRunBlock.Block);

            // Implement the `check_has_run_cctor` block.
            WithMetadata(
                BuildStore(checkHasRunBlock.Builder, ConstInt(Int1Type(), 1, false), isRunningHerePtr),
                invariantGroupMdId,
                invariantGroup);

            BuildCondBr(
                checkHasRunBlock.Builder,
                IntToBoolean(
                    checkHasRunBlock.Builder,
                    BuildAtomicLoad(checkHasRunBlock.Builder, hasRunPtr, "has_run_cctor")),
                afterBlock.Block,
                waitForLockBlock.Block);

            // Implement the `wait_for_cctor_lock` block.
            var cmpxhg = BuildAtomicCmpXchg(
                waitForLockBlock.Builder,
                isRunningPtr,
                ConstInt(Int8Type(), 0, false),
                ConstInt(Int8Type(), 1, false),
                LLVMAtomicOrdering.LLVMAtomicOrderingSequentiallyConsistent,
                LLVMAtomicOrdering.LLVMAtomicOrderingMonotonic,
                false);

            cmpxhg.SetValueName("is_running_cctor_cmpxhg");

            BuildCondBr(
                waitForLockBlock.Builder,
                BuildExtractValue(
                    waitForLockBlock.Builder,
                    cmpxhg,
                    1,
                    "has_exchanged"),
                maybeRunBlock.Block,
                waitForLockBlock.Block);

            // Implement the `maybe_run_cctor` block.
            BuildCondBr(
                maybeRunBlock.Builder,
                IntToBoolean(
                    maybeRunBlock.Builder,
                    BuildAtomicLoad(maybeRunBlock.Builder, hasRunPtr, "has_run_cctor")),
                resetLockBlock.Block,
                runBlock.Block);

            // Implement the `run_cctor` block.
            for (int i = 0; i < Type.StaticConstructors.Count; i++)
            {
                BuildCall(runBlock.Builder, Declare(Type.StaticConstructors[i]), new LLVMValueRef[] { }, "");
            }
            BuildAtomicStore(runBlock.Builder, ConstInt(Int8Type(), 1, false), hasRunPtr);
            BuildBr(runBlock.Builder, resetLockBlock.Block);

            // Implement the `reset_lock` block.
            BuildAtomicStore(resetLockBlock.Builder, ConstInt(Int8Type(), 0, false), isRunningPtr);
            BuildBr(resetLockBlock.Builder, afterBlock.Block);

            return(afterBlock);
        }
Exemple #27
0
        private void ThreadJumps(BasicBlockBuilder block, HashSet <BasicBlockTag> processedBlocks)
        {
            if (!processedBlocks.Add(block))
            {
                return;
            }

            var flow = block.Flow;

            if (flow is JumpFlow)
            {
                // Jump flow is usually fairly straightforward to thread.
                var jumpFlow   = (JumpFlow)flow;
                var threadFlow = AsThreadableFlow(jumpFlow.Branch, block.Graph, processedBlocks);
                if (threadFlow != null && jumpFlow.Branch.Target != block.Tag)
                {
                    block.Flow = threadFlow;

                    if (block.Flow is SwitchFlow)
                    {
                        // If the jump flow gets replaced by switch flow, then we want to
                        // jump-thread the block again.
                        processedBlocks.Remove(block);
                        ThreadJumps(block, processedBlocks);
                        return;
                    }
                }

                // Now would also be a good time to try and fuse this block with
                // its successor, if this jump is the only branch to the successor.
                var           predecessors = block.Graph.GetAnalysisResult <BasicBlockPredecessors>();
                BasicBlockTag tail;
                if (BlockFusion.TryGetFusibleTail(block, block.Graph.ToImmutable(), predecessors, out tail))
                {
                    // Fuse the blocks.
                    FuseBlocks(block, tail);

                    // Fusing these blocks may have exposed additional information
                    // that will enable us to thread more jumps.
                    processedBlocks.Remove(block);
                    ThreadJumps(block, processedBlocks);
                }
            }
            else if (flow is TryFlow)
            {
                // We might be able to turn try flow into a jump, which we can thread
                // recursively.
                var tryFlow = (TryFlow)flow;

                // Start off by threading the try flow's branches.
                var successBranch = tryFlow.SuccessBranch;
                var failureBranch = tryFlow.ExceptionBranch;
                var successFlow   = AsThreadableFlow(tryFlow.SuccessBranch, block.Graph, processedBlocks);
                var failureFlow   = AsThreadableFlow(tryFlow.ExceptionBranch, block.Graph, processedBlocks);
                if (successFlow is JumpFlow)
                {
                    successBranch = ((JumpFlow)successFlow).Branch;
                }
                if (failureFlow is JumpFlow)
                {
                    failureBranch = ((JumpFlow)failureFlow).Branch;
                }

                var exceptionSpecs = block.Graph.GetAnalysisResult <InstructionExceptionSpecs>();
                if (!exceptionSpecs.GetExceptionSpecification(tryFlow.Instruction).CanThrowSomething ||
                    IsRethrowBranch(failureBranch, block.Graph, processedBlocks))
                {
                    // If the "risky" instruction absolutely cannot throw, then we can
                    // just rewrite the try as a jump.
                    // Similarly, if the exception branch simply re-throws the exception,
                    // then we are also free to make the same transformation, but for
                    // different reasons.
                    var value  = block.AppendInstruction(tryFlow.Instruction);
                    var branch = successBranch.WithArguments(
                        successBranch.Arguments
                        .Select(arg => arg.IsTryResult ? BranchArgument.FromValue(value) : arg)
                        .ToArray());
                    block.Flow = new JumpFlow(branch);

                    // Jump-thread this block again.
                    processedBlocks.Remove(block);
                    ThreadJumps(block, processedBlocks);
                }
                else if (IsRethrowIntrinsic(tryFlow.Instruction))
                {
                    // If the "risky" instruction is really just a 'rethrow' intrinsic,
                    // then we can branch directly to the exception branch.

                    var capturedException = tryFlow.Instruction.Arguments[0];
                    JumpToExceptionBranch(block, failureBranch, capturedException);

                    // Jump-thread this block again.
                    processedBlocks.Remove(block);
                    ThreadJumps(block, processedBlocks);
                }
                else if (IsThrowIntrinsic(tryFlow.Instruction))
                {
                    // If the "risky" instruction is really just a 'throw' intrinsic,
                    // then we can insert an instruction to capture the exception and
                    // jump directly to the exception block.
                    var exception = tryFlow.Instruction.Arguments[0];

                    var capturedExceptionParam = failureBranch
                                                 .ZipArgumentsWithParameters(block.Graph)
                                                 .FirstOrDefault(pair => pair.Value.IsTryException)
                                                 .Key;

                    if (capturedExceptionParam == null)
                    {
                        // If the exception branch does not pass an '#exception' parameter,
                        // then we can replace the try by a simple jump.
                        block.Flow = new JumpFlow(failureBranch);
                    }
                    else
                    {
                        // Otherwise, we actually need to capture the exception.
                        var capturedException = block.AppendInstruction(
                            Instruction.CreateCaptureIntrinsic(
                                block.Graph.GetValueType(capturedExceptionParam),
                                block.Graph.GetValueType(exception),
                                exception));

                        JumpToExceptionBranch(block, failureBranch, capturedException);
                    }

                    // Jump-thread this block again.
                    processedBlocks.Remove(block);
                    ThreadJumps(block, processedBlocks);
                }
                else
                {
                    // If we can't replace the 'try' flow with something better, then
                    // the least we can do is try and thread the 'try' flow's branches.
                    block.Flow = new TryFlow(tryFlow.Instruction, successBranch, failureBranch);
                }
            }
            else if (flow is SwitchFlow && IncludeSwitches)
            {
                // If we're allowed to, then we might be able to thread jumps in switch flow
                // branches.
                bool changed    = false;
                var  switchFlow = (SwitchFlow)flow;

                // Rewrite switch cases.
                var cases = new List <SwitchCase>();
                foreach (var switchCase in switchFlow.Cases)
                {
                    var caseFlow = AsThreadableFlow(switchCase.Branch, block.Graph, processedBlocks);
                    if (caseFlow != null &&
                        switchCase.Branch.Target != block.Tag)
                    {
                        if (caseFlow is JumpFlow)
                        {
                            cases.Add(
                                new SwitchCase(
                                    switchCase.Values,
                                    ((JumpFlow)caseFlow).Branch));
                            changed = true;
                            continue;
                        }
                        else if (caseFlow is SwitchFlow)
                        {
                            var threadedSwitch = (SwitchFlow)caseFlow;
                            if (threadedSwitch.SwitchValue == switchFlow.SwitchValue)
                            {
                                var valuesToBranches = threadedSwitch.ValueToBranchMap;
                                foreach (var value in switchCase.Values)
                                {
                                    Branch branchForValue;
                                    if (!valuesToBranches.TryGetValue(value, out branchForValue))
                                    {
                                        branchForValue = threadedSwitch.DefaultBranch;
                                    }
                                    cases.Add(
                                        new SwitchCase(
                                            ImmutableHashSet.Create(value),
                                            branchForValue));
                                }
                                changed = true;
                                continue;
                            }
                        }
                    }
                    cases.Add(switchCase);
                }

                // Rewrite default branch if possible.
                var defaultBranch = switchFlow.DefaultBranch;
                if (defaultBranch.Target != block.Tag)
                {
                    var defaultFlow = AsThreadableFlow(defaultBranch, block.Graph, processedBlocks);
                    if (defaultFlow is JumpFlow)
                    {
                        defaultBranch = ((JumpFlow)defaultFlow).Branch;
                        changed       = true;
                    }
                    else if (defaultFlow is SwitchFlow)
                    {
                        var threadedSwitch = (SwitchFlow)defaultFlow;
                        if (threadedSwitch.SwitchValue == switchFlow.SwitchValue)
                        {
                            var valueSet = ImmutableHashSet.CreateRange(switchFlow.ValueToBranchMap.Keys);
                            foreach (var switchCase in threadedSwitch.Cases)
                            {
                                cases.Add(
                                    new SwitchCase(
                                        switchCase.Values.Except(valueSet),
                                        switchCase.Branch));
                            }
                            defaultBranch = threadedSwitch.DefaultBranch;
                            changed       = true;
                        }
                    }
                }

                if (changed)
                {
                    block.Flow = new SwitchFlow(
                        switchFlow.SwitchValue,
                        cases,
                        defaultBranch);
                }

                // Also simplify the block's switch flow. If the switch
                // flow decays to jump flow, then we want to try threading
                // this block again.
                if (SwitchSimplification.TrySimplifySwitchFlow(block) &&
                    block.Flow is JumpFlow)
                {
                    processedBlocks.Remove(block);
                    ThreadJumps(block, processedBlocks);
                }
            }
        }
Exemple #28
0
        private static bool InternalTryCompileExpression(
            ExpressionSyntax syntax,
            CompiledMethod method,
            BasicBlockBuilder builder,
            INameResolver nameResolver,
            IDiagnosticSink diagnostics,
            out Temporary value)
        {
            value = default;

            if (syntax is IntegerLiteralSyntax integer)
            {
                // Decide the type for the literal
                // TODO: There is for now only support for int32 and uint32, and only because the smallest int32 should be
                // TODO: representable. This logic should be updated once multiple integer types officially exist.
                if (integer.Value > uint.MaxValue)
                {
                    diagnostics.Add(DiagnosticCode.IntegerConstantOutOfBounds, syntax.Position,
                                    integer.Value.ToString(), SimpleType.Int32.TypeName);
                    return(false);
                }
                else if (integer.Value > int.MaxValue)
                {
                    // TODO: This does not work for anything else than int32 minimum
                    value = Temporary.FromConstant(SimpleType.UInt32, ConstantValue.SignedInteger((long)integer.Value));
                    return(true);
                }
                else
                {
                    value = Temporary.FromConstant(SimpleType.Int32, ConstantValue.SignedInteger((long)integer.Value));
                    return(true);
                }
            }
            else if (syntax is BooleanLiteralSyntax boolean)
            {
                value = Temporary.FromConstant(SimpleType.Bool, ConstantValue.Bool(boolean.Value));
                return(true);
            }
            else if (syntax is IdentifierSyntax named)
            {
                if (!nameResolver.TryResolveVariable(named.Name, out var valueNumber))
                {
                    diagnostics.Add(DiagnosticCode.VariableNotFound, named.Position, named.Name);
                    return(false);
                }

                value = Temporary.FromLocal(method.Values[valueNumber].Type, (ushort)valueNumber);
                return(true);
            }
            else if (syntax is UnaryExpressionSyntax unary)
            {
                if (!InternalTryCompileExpression(unary.InnerExpression, method, builder, nameResolver, diagnostics,
                                                  out var inner))
                {
                    return(false);
                }

                return(TryCompileUnary(unary, inner, method, builder, diagnostics, out value));
            }
            else if (syntax is BinaryExpressionSyntax binary)
            {
                if (!InternalTryCompileExpression(binary.Left, method, builder, nameResolver, diagnostics, out var left) ||
                    !InternalTryCompileExpression(binary.Right, method, builder, nameResolver, diagnostics, out var right))
                {
                    return(false);
                }

                return(TryCompileBinary(binary, left, right, method, builder, diagnostics, out value));
            }
            else if (syntax is FunctionCallSyntax call)
            {
                return(TryCompileCall(call, method, builder, diagnostics, nameResolver, out value));
            }
            else
            {
                throw new NotImplementedException();
            }
        }
Exemple #29
0
 /// <inheritdoc/>
 public override InstructionBuilder GetInstructionBuilder(BasicBlockBuilder block, int instructionIndex)
 {
     throw new IndexOutOfRangeException();
 }
Exemple #30
0
        private static bool TryCompileCall(FunctionCallSyntax call, CompiledMethod method, BasicBlockBuilder builder,
                                           IDiagnosticSink diagnostics, INameResolver nameResolver, out Temporary value)
        {
            value = default;

            // Get the callee
            var matchingMethods = nameResolver.ResolveMethod(call.Function.Name);

            if (matchingMethods.Count == 0)
            {
                diagnostics.Add(DiagnosticCode.MethodNotFound, call.Function.Position, call.Function.Name);
                return(false);
            }
            else if (matchingMethods.Count > 1)
            {
                // TODO: Test this case
                throw new NotImplementedException("Multiple matching methods");
            }
            var declaration = matchingMethods[0];

            // Assert that there is the right number of parameters
            if (call.Parameters.Count != declaration.ParameterTypes.Count)
            {
                diagnostics.Add(DiagnosticCode.ParameterCountMismatch, call.Position,
                                actual: call.Parameters.Count.ToString(), expected: declaration.ParameterTypes.Count.ToString());
                return(false);
            }

            // Evaluate the parameters, verifying their types
            var parameterIndices = new int[declaration.ParameterTypes.Count];

            for (var i = 0; i < parameterIndices.Length; i++)
            {
                var paramIndex = TryCompileExpression(call.Parameters[i], declaration.ParameterTypes[i],
                                                      method, builder, nameResolver, diagnostics);

                // If the compilation of the expression failed for some reason, the diagnostic is already logged
                if (paramIndex == -1)
                {
                    return(false);
                }
                parameterIndices[i] = paramIndex;
            }

            // Emit a call operation
            var callType = declaration is ImportedMethodDeclaration ? MethodCallType.Imported : MethodCallType.Native;

            var callInfoIndex = method.AddCallInfo(declaration.BodyIndex, parameterIndices, declaration.FullName, callType);
            var resultIndex   = method.AddLocal(declaration.ReturnType, LocalFlags.None);

            builder.AppendInstruction(Opcode.Call, callInfoIndex, 0, resultIndex);

            value = Temporary.FromLocal(declaration.ReturnType, resultIndex);
            return(true);
        }