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); }
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); }
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); }
private bool TryCompileIf(IfStatementSyntax ifSyntax, BasicBlockBuilder builder, out BasicBlockBuilder newBuilder, out bool returnGuaranteed) { newBuilder = builder; // Default failure case returnGuaranteed = false; Debug.Assert(_methodInProgress != null); // Compile the condition expression var conditionValue = ExpressionCompiler.TryCompileExpression(ifSyntax.ConditionSyntax, SimpleType.Bool, _methodInProgress, builder, this, _diagnostics); if (conditionValue == -1) { return(false); } // Compile the 'then' branch var thenBuilder = builder.CreateBranch((ushort)conditionValue); if (!TryCompileBlock(ifSyntax.ThenBlockSyntax, thenBuilder, out thenBuilder, out var thenReturns)) { return(false); } // Compile the possible 'else' branch. // It can be either an IfStatementSyntax (else if), BlockSyntax (else) or null (no else). if (ifSyntax.ElseSyntax is null) { // The block where the control flow merges back must not contain a critical edge, where // a branch leads to a block with several predecessors. If the 'then' block returns, this // won't happen; otherwise we must create a block for the negative branch. // This is required for the SSA form because PHIs in the merger block must be resolved // at the predecessors. if (thenReturns) { // Happy case: the 'then' branch does not merge back newBuilder = builder.CreateSuccessorBlock(); } else { // This block will contain PHI resolution for the pre-if values if newBuilder contains PHIs var negativeLandingBlock = builder.CreateSuccessorBlock(); // This block is the real merger block newBuilder = negativeLandingBlock.CreateSuccessorBlock(); thenBuilder.SetSuccessor(newBuilder.Index); } // Here, we cannot give a return guarantee unless the 'then' branch is always taken and returns // TODO: Return guarantee for const conditions return(true); } else if (ifSyntax.ElseSyntax is BlockSyntax elseBlock) { // Compile the else block var elseBuilder = builder.CreateSuccessorBlock(); if (!TryCompileBlock(elseBlock, elseBuilder, out elseBuilder, out var elseReturns)) { return(false); } // Then create a new block that is the target of both the 'then' and 'else' blocks newBuilder = elseBuilder.CreateSuccessorBlock(); thenBuilder.SetSuccessor(newBuilder.Index); returnGuaranteed = thenReturns & elseReturns; return(true); } else if (ifSyntax.ElseSyntax is IfStatementSyntax elseIf) { // Compile the if, then make the 'then' block point to the created successor if (!TryCompileIf(elseIf, builder.CreateSuccessorBlock(), out newBuilder, out var elseIfReturns)) { return(false); } thenBuilder.SetSuccessor(newBuilder.Index); returnGuaranteed = thenReturns & elseIfReturns; return(true); } else { throw new InvalidOperationException("Invalid syntax node type for 'else'."); } }
private bool TryCompileBlock(BlockSyntax block, BasicBlockBuilder builder, out BasicBlockBuilder newBuilder, out bool returnGuaranteed) { // Some statements may create new basic blocks, but the default is that they do not newBuilder = builder; returnGuaranteed = false; var deadCodeWarningEmitted = false; // Create a new variable scope _variableMap.PushScope(); foreach (var statement in block.Statements) { // Emit a dead code warning if return is already guaranteed if (returnGuaranteed && !deadCodeWarningEmitted) { _diagnostics.Add(DiagnosticCode.UnreachableCode, statement.Position); deadCodeWarningEmitted = true; } switch (statement) { case AssignmentSyntax assignment: if (!TryCompileAssignment(assignment, builder)) { return(false); } break; case BlockSyntax innerBlock: if (!TryCompileBlock(innerBlock, builder, out newBuilder, out var blockReturns)) { return(false); } builder = newBuilder; returnGuaranteed |= blockReturns; break; case FunctionCallStatementSyntax call: // Call statements are call expressions with the result ignored Debug.Assert(_methodInProgress != null); if (ExpressionCompiler.TryCompileExpression(call.Call, null, _methodInProgress, builder, this, _diagnostics) == -1) { return(false); } break; case IfStatementSyntax ifStatement: if (!TryCompileIf(ifStatement, builder, out newBuilder, out var ifReturns)) { return(false); } builder = newBuilder; returnGuaranteed |= ifReturns; break; case ReturnStatementSyntax returnSyntax: if (!TryCompileReturn(returnSyntax, builder)) { return(false); } returnGuaranteed = true; break; case VariableDeclarationSyntax variableDeclaration: if (!TryCompileVariableDeclaration(variableDeclaration, builder)) { return(false); } break; case WhileStatementSyntax whileStatement: if (!TryCompileWhile(whileStatement, builder, out newBuilder)) { return(false); } builder = newBuilder; break; default: throw new NotImplementedException("Unimplemented statement type"); } } // Destroy the variable scope // TODO: Destroy locals (both variables and temporaries!) defined with destructor _variableMap.PopScope(); return(true); }