/// <summary> /// Builds an assert implementation that calls a nested fail function based on /// a boolean condition (first parameter). /// </summary> protected static Method BuildDebugAssertImplementation( IRContext irContext, MethodBase debugAssertMethod, MethodBase assertFailedMethod) { // Create a call to the debug-implementation wrapper while taking the // current source location into account var method = irContext.Declare(debugAssertMethod, out bool created); if (!created) { return(method); } var location = Location.Nowhere; using var builder = method.CreateBuilder(); method.AddFlags(MethodFlags.Inline); // Create the entry, body and exit blocks var entryBlock = builder.EntryBlockBuilder; var bodyBlock = builder.CreateBasicBlock(location); var exitBlock = builder.CreateBasicBlock(location); // Initialize the parameters var sourceParameters = debugAssertMethod.GetParameters(); var parameters = InlineList <Parameter> .Create(sourceParameters.Length); foreach (var parameter in sourceParameters) { var paramType = entryBlock.CreateType(parameter.ParameterType); parameters.Add(builder.AddParameter(paramType, parameter.Name)); } // Check condition entryBlock.CreateIfBranch( location, parameters[0], exitBlock, bodyBlock); // Fill the body var assertFailed = bodyBlock.CreateCall( location, irContext.Declare(assertFailedMethod, out var _)); for (int i = 1; i < parameters.Count; ++i) { assertFailed.Add(parameters[i]); } assertFailed.Seal(); bodyBlock.CreateBranch(location, exitBlock); // Create return exitBlock.CreateReturn(location); return(method); }
/// <summary> /// Maps internal <see cref="WriteToOutput"/> values to /// <see cref="PrintF(string)"/> method calls. /// </summary> protected override void Implement( IRContext context, Method.Builder methodBuilder, BasicBlock.Builder builder, WriteToOutput writeToOutput) { var location = writeToOutput.Location; // Convert to format string constant var expressionString = writeToOutput.ToEscapedPrintFExpression(); var expression = builder.CreatePrimitiveValue( location, expressionString); // Create a call to the native printf var printFMethod = context.Declare(PrintFMethod, out bool _); var callBuilder = builder.CreateCall(location, printFMethod); callBuilder.Add(expression); foreach (Value argument in writeToOutput.Arguments) { var converted = WriteToOutput.ConvertToPrintFArgument( builder, location, argument); callBuilder.Add(converted); } // Replace the write node with the call callBuilder.Seal(); builder.Remove(writeToOutput); }
/// <summary> /// Maps internal <see cref="WriteToOutput"/> values to /// <see cref="PrintF(string, void*)"/> method calls. /// </summary> protected override void Implement( IRContext context, Method.Builder methodBuilder, BasicBlock.Builder builder, WriteToOutput writeToOutput) { var location = writeToOutput.Location; // Convert to format string constant var expressionString = writeToOutput.ToPrintFExpression(); var expression = builder.CreatePrimitiveValue( location, expressionString, Encoding.ASCII); // Create an argument structure that can be passed via local memory var argumentBuilder = builder.CreateDynamicStructure( location, writeToOutput.Count); foreach (Value argument in writeToOutput.Arguments) { var converted = WriteToOutput.ConvertToPrintFArgument( builder, location, argument); argumentBuilder.Add(converted); } var argumentStructure = argumentBuilder.Seal(); // Create local alloca to store all data var alloca = builder.CreateAlloca( location, argumentStructure.Type, MemoryAddressSpace.Local); // Store structure into chunk of local memory builder.CreateStore(location, alloca, argumentStructure); // Cast alloca to the generic address space to satisfy the requirements of // of the printf method alloca = builder.CreateAddressSpaceCast( location, alloca, MemoryAddressSpace.Generic); // Create a call to the native printf var printFMethod = context.Declare(PrintFMethod, out bool _); var callBuilder = builder.CreateCall(location, printFMethod); callBuilder.Add(expression); callBuilder.Add(alloca); // Replace the write node with the call callBuilder.Seal(); builder.Remove(writeToOutput); }
/// <summary> /// Maps internal debug assertions to <see cref="AssertFailed(string, string, /// int, string, int)"/> method calls. /// </summary> protected override void Implement( IRContext context, Method.Builder methodBuilder, BasicBlock.Builder builder, DebugAssertOperation debugAssert) { var location = debugAssert.Location; // Create a call to the debug-implementation wrapper while taking the // current source location into account var nextBlock = builder.SplitBlock(debugAssert); var innerBlock = methodBuilder.CreateBasicBlock( location, nameof(AssertFailed)); builder.CreateIfBranch( location, debugAssert.Condition, nextBlock, innerBlock); // Create a call to the assert implementation var innerBuilder = methodBuilder[innerBlock]; var assertFailed = innerBuilder.CreateCall( location, context.Declare(AssertFailedMethod, out var _)); // Move the debug assertion to this block var sourceMessage = debugAssert.Message.ResolveAs <StringValue>(); var message = innerBuilder.CreatePrimitiveValue( location, sourceMessage.String, sourceMessage.Encoding); assertFailed.Add(message); // Append source location information var debugLocation = debugAssert.GetLocationInfo(); assertFailed.Add( innerBuilder.CreatePrimitiveValue(location, debugLocation.FileName)); assertFailed.Add( innerBuilder.CreatePrimitiveValue(location, debugLocation.Line)); assertFailed.Add( innerBuilder.CreatePrimitiveValue(location, debugLocation.Method)); assertFailed.Add( innerBuilder.CreatePrimitiveValue(location, 1)); // Finish the actual assertion call and branch assertFailed.Seal(); innerBuilder.CreateBranch(location, nextBlock); // Remove the debug assertion value debugAssert.Replace(builder.CreateUndefined()); }