* Add source line debug metadata. Works with javascript output.

* Add names of arguments and locals when they're stored on the stack (and thus easily visible in the debugger)

* Add debugging information to documentation
This commit is contained in:
Morgan Brown 2018-08-22 16:04:53 -07:00 коммит произвёл GitHub
Родитель 6fdbbbadad
Коммит 2d4981ac9e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
9 изменённых файлов: 248 добавлений и 11 удалений

Просмотреть файл

@ -37,6 +37,7 @@ This is Windows only for now.
# Useful tips #
* To manually make ILC compile to WebAssembly, add ```--wasm``` to the command line.
* To debug C# source, add ```-g4``` to the emcc command line and change ```-s WASM=1``` to ```-s WASM=0```. This will generate a JavaScript source map that browser debuggers and Visual Studio Code can work with. Using Visual Studio Code's Chrome debugger works particularly well.
* Add ```-g3``` to the emcc command line to generate more debuggable output and a .wast file with the text form of the WebAssembly.
* Omit ```-s WASM=1``` from the emcc command line to generate asm.js. Browser debuggers currently work better with asm.js and it's often a bit more readable than wast.
* Change ```-s WASM=1``` to ```-s WASM=0``` in the emcc command line to generate asm.js. Browser debuggers currently work better with asm.js and it's often a bit more readable than wast.
* Add ```-O2 --llvm-lto 2``` to the emcc command line to enable optimizations. This makes the generated WebAssembly as much as 75% smaller as well as more efficient.

Просмотреть файл

@ -0,0 +1,23 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Text;
using LLVMSharp;
namespace ILCompiler.WebAssembly
{
class DebugMetadata
{
public DebugMetadata(LLVMMetadataRef file, LLVMMetadataRef compileUnit)
{
File = file;
CompileUnit = compileUnit;
}
public LLVMMetadataRef CompileUnit { get; }
public LLVMMetadataRef File { get; }
}
}

Просмотреть файл

@ -5,6 +5,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Internal.TypeSystem;
using ILCompiler;
@ -12,8 +14,8 @@ using LLVMSharp;
using ILCompiler.CodeGen;
using ILCompiler.DependencyAnalysis;
using ILCompiler.DependencyAnalysisFramework;
using ILCompiler.WebAssembly;
using Internal.TypeSystem.Ecma;
using System.Linq;
namespace Internal.IL
{
@ -35,6 +37,7 @@ namespace Internal.IL
}
public LLVMModuleRef Module { get; }
public LLVMContextRef Context { get; }
private readonly MethodDesc _method;
private readonly MethodIL _methodIL;
private readonly MethodSignature _signature;
@ -49,6 +52,8 @@ namespace Internal.IL
private List<SpilledExpressionEntry> _spilledExpressions = new List<SpilledExpressionEntry>();
private int _pointerSize;
private readonly byte[] _ilBytes;
private MethodDebugInformation _debugInformation;
private LLVMMetadataRef _debugFunction;
/// <summary>
/// Stack of values pushed onto the IL stack: locals, arguments, values, function pointer, ...
@ -106,6 +111,10 @@ namespace Internal.IL
_llvmFunction = GetOrCreateLLVMFunction(mangledName, method.Signature);
_builder = LLVM.CreateBuilder();
_pointerSize = compilation.NodeFactory.Target.PointerSize;
_debugInformation = _compilation.GetDebugInfo(_methodIL);
Context = LLVM.GetModuleContext(Module);
}
public void Import()
@ -174,22 +183,57 @@ namespace Internal.IL
signatureIndex++;
}
string[] argNames = null;
if (_debugInformation != null)
{
argNames = _debugInformation.GetParameterNames()?.ToArray();
}
for (int i = 0; i < _signature.Length; i++)
{
if (CanStoreTypeOnStack(_signature[i]))
{
LLVMValueRef argStackSlot = LLVM.BuildAlloca(_builder, GetLLVMTypeForTypeDesc(_signature[i]), $"arg{i + thisOffset}_");
string argName = String.Empty;
if (argNames != null && argNames[i] != null)
{
argName = argNames[i] + "_";
}
argName += $"arg{i + thisOffset}_";
LLVMValueRef argStackSlot = LLVM.BuildAlloca(_builder, GetLLVMTypeForTypeDesc(_signature[i]), argName);
LLVM.BuildStore(_builder, LLVM.GetParam(_llvmFunction, (uint)signatureIndex), argStackSlot);
_argSlots[i] = argStackSlot;
signatureIndex++;
}
}
string[] localNames = new string[_locals.Length];
if (_debugInformation != null)
{
foreach (ILLocalVariable localDebugInfo in _debugInformation.GetLocalVariables() ?? Enumerable.Empty<ILLocalVariable>())
{
// Check whether the slot still exists as the compiler may remove it for intrinsics
int slot = localDebugInfo.Slot;
if (slot < localNames.Length)
{
localNames[localDebugInfo.Slot] = localDebugInfo.Name;
}
}
}
for (int i = 0; i < _locals.Length; i++)
{
if (CanStoreLocalOnStack(_locals[i].Type))
{
LLVMValueRef localStackSlot = LLVM.BuildAlloca(_builder, GetLLVMTypeForTypeDesc(_locals[i].Type), $"local{i}_");
string localName = String.Empty;
if (localNames[i] != null)
{
localName = localNames[i] + "_";
}
localName += $"local{i}_";
LLVMValueRef localStackSlot = LLVM.BuildAlloca(_builder, GetLLVMTypeForTypeDesc(_locals[i].Type), localName);
_localSlots[i] = localStackSlot;
}
}
@ -398,10 +442,69 @@ namespace Internal.IL
private void StartImportingInstruction()
{
if (_debugInformation != null)
{
bool foundSequencePoint = false;
ILSequencePoint curSequencePoint = default;
foreach (var sequencePoint in _debugInformation.GetSequencePoints() ?? Enumerable.Empty<ILSequencePoint>())
{
if (sequencePoint.Offset == _currentOffset)
{
curSequencePoint = sequencePoint;
foundSequencePoint = true;
break;
}
else if (sequencePoint.Offset < _currentOffset)
{
curSequencePoint = sequencePoint;
foundSequencePoint = true;
}
}
if (!foundSequencePoint)
{
return;
}
// LLVM can't process empty string file names
if (String.IsNullOrWhiteSpace(curSequencePoint.Document))
{
return;
}
DebugMetadata debugMetadata;
if (!_compilation.DebugMetadataMap.TryGetValue(curSequencePoint.Document, out debugMetadata))
{
string fullPath = curSequencePoint.Document;
string fileName = Path.GetFileName(fullPath);
string directory = Path.GetDirectoryName(fullPath) ?? String.Empty;
LLVMMetadataRef fileMetadata = LLVMPInvokes.LLVMDIBuilderCreateFile(_compilation.DIBuilder, fullPath, fullPath.Length,
directory, directory.Length);
// todo: get the right value for isOptimized
LLVMMetadataRef compileUnitMetadata = LLVMPInvokes.LLVMDIBuilderCreateCompileUnit(_compilation.DIBuilder, LLVMDWARFSourceLanguage.LLVMDWARFSourceLanguageC,
fileMetadata, "ILC", 3, isOptimized: false, String.Empty, 0, 1, String.Empty, 0, LLVMDWARFEmissionKind.LLVMDWARFEmissionFull, 0, false, false);
LLVM.AddNamedMetadataOperand(Module, "llvm.dbg.cu", LLVM.MetadataAsValue(Context, compileUnitMetadata));
debugMetadata = new DebugMetadata(fileMetadata, compileUnitMetadata);
_compilation.DebugMetadataMap[fullPath] = debugMetadata;
}
if (_debugFunction.Pointer == IntPtr.Zero)
{
_debugFunction = LLVM.DIBuilderCreateFunction(_compilation.DIBuilder, debugMetadata.CompileUnit, _method.Name, String.Empty, debugMetadata.File,
(uint)_debugInformation.GetSequencePoints().FirstOrDefault().LineNumber, default(LLVMMetadataRef), 1, 1, 1, 0, IsOptimized: 0, _llvmFunction);
}
LLVMMetadataRef currentLine = LLVMPInvokes.LLVMDIBuilderCreateDebugLocation(Context, (uint)curSequencePoint.LineNumber, 0, _debugFunction, default(LLVMMetadataRef));
LLVM.SetCurrentDebugLocation(_builder, LLVM.MetadataAsValue(Context, currentLine));
}
}
private void EndImportingInstruction()
{
// Reset the debug position so it doesn't end up applying to the wrong instructions
LLVM.SetCurrentDebugLocation(_builder, default(LLVMValueRef));
}
private void ImportNop()

Просмотреть файл

@ -0,0 +1,78 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
using LLVMSharp;
namespace ILCompiler.WebAssembly
{
// LLVM P/Invokes copied from LLVMSharp that match the current LLVM surface area.
// If we get a new version of LLVMSharp containing these, this file should be removed.
internal class LLVMPInvokes
{
const string libraryPath = "libLLVM";
[DllImport(libraryPath, CallingConvention = CallingConvention.Cdecl)]
public static extern LLVMDIBuilderRef LLVMCreateDIBuilder(LLVMModuleRef M);
[DllImport(libraryPath, EntryPoint = "LLVMDIBuilderCreateCompileUnit", CallingConvention = CallingConvention.Cdecl)]
public static extern LLVMMetadataRef LLVMDIBuilderCreateCompileUnit(LLVMDIBuilderRef @Builder, LLVMDWARFSourceLanguage @Lang, LLVMMetadataRef @FileRef, [MarshalAs(UnmanagedType.LPStr)] string @Producer, size_t @ProducerLen, LLVMBool @isOptimized, [MarshalAs(UnmanagedType.LPStr)] string @Flags, size_t @FlagsLen, uint @RuntimeVer, [MarshalAs(UnmanagedType.LPStr)] string @SplitName, size_t @SplitNameLen, LLVMDWARFEmissionKind @Kind, uint @DWOId, LLVMBool @SplitDebugInlining, LLVMBool @DebugInfoForProfiling);
[DllImport(libraryPath, EntryPoint = "LLVMDIBuilderCreateFile", CallingConvention = CallingConvention.Cdecl)]
public static extern LLVMMetadataRef LLVMDIBuilderCreateFile(LLVMDIBuilderRef @Builder, [MarshalAs(UnmanagedType.LPStr)] string @Filename, size_t @FilenameLen, [MarshalAs(UnmanagedType.LPStr)] string @Directory, size_t @DirectoryLen);
[DllImport(libraryPath, EntryPoint = "LLVMDIBuilderCreateDebugLocation", CallingConvention = CallingConvention.Cdecl)]
public static extern LLVMMetadataRef LLVMDIBuilderCreateDebugLocation(LLVMContextRef @Ctx, uint @Line, uint @Column, LLVMMetadataRef @Scope, LLVMMetadataRef @InlinedAt);
}
internal enum LLVMDWARFSourceLanguage : int
{
@LLVMDWARFSourceLanguageC89 = 0,
@LLVMDWARFSourceLanguageC = 1,
@LLVMDWARFSourceLanguageAda83 = 2,
@LLVMDWARFSourceLanguageC_plus_plus = 3,
@LLVMDWARFSourceLanguageCobol74 = 4,
@LLVMDWARFSourceLanguageCobol85 = 5,
@LLVMDWARFSourceLanguageFortran77 = 6,
@LLVMDWARFSourceLanguageFortran90 = 7,
@LLVMDWARFSourceLanguagePascal83 = 8,
@LLVMDWARFSourceLanguageModula2 = 9,
@LLVMDWARFSourceLanguageJava = 10,
@LLVMDWARFSourceLanguageC99 = 11,
@LLVMDWARFSourceLanguageAda95 = 12,
@LLVMDWARFSourceLanguageFortran95 = 13,
@LLVMDWARFSourceLanguagePLI = 14,
@LLVMDWARFSourceLanguageObjC = 15,
@LLVMDWARFSourceLanguageObjC_plus_plus = 16,
@LLVMDWARFSourceLanguageUPC = 17,
@LLVMDWARFSourceLanguageD = 18,
@LLVMDWARFSourceLanguagePython = 19,
@LLVMDWARFSourceLanguageOpenCL = 20,
@LLVMDWARFSourceLanguageGo = 21,
@LLVMDWARFSourceLanguageModula3 = 22,
@LLVMDWARFSourceLanguageHaskell = 23,
@LLVMDWARFSourceLanguageC_plus_plus_03 = 24,
@LLVMDWARFSourceLanguageC_plus_plus_11 = 25,
@LLVMDWARFSourceLanguageOCaml = 26,
@LLVMDWARFSourceLanguageRust = 27,
@LLVMDWARFSourceLanguageC11 = 28,
@LLVMDWARFSourceLanguageSwift = 29,
@LLVMDWARFSourceLanguageJulia = 30,
@LLVMDWARFSourceLanguageDylan = 31,
@LLVMDWARFSourceLanguageC_plus_plus_14 = 32,
@LLVMDWARFSourceLanguageFortran03 = 33,
@LLVMDWARFSourceLanguageFortran08 = 34,
@LLVMDWARFSourceLanguageRenderScript = 35,
@LLVMDWARFSourceLanguageBLISS = 36,
@LLVMDWARFSourceLanguageMips_Assembler = 37,
@LLVMDWARFSourceLanguageGOOGLE_RenderScript = 38,
@LLVMDWARFSourceLanguageBORLAND_Delphi = 39,
}
internal enum LLVMDWARFEmissionKind : int
{
@LLVMDWARFEmissionNone = 0,
@LLVMDWARFEmissionFull = 1,
@LLVMDWARFEmissionLineTablesOnly = 2,
}
}

Просмотреть файл

@ -144,6 +144,8 @@ namespace ILCompiler.DependencyAnalysis
// this is the llvm instance.
public LLVMModuleRef Module { get; }
public LLVMDIBuilderRef DIBuilder { get; }
// This is used to build mangled names
private Utf8StringBuilder _sb = new Utf8StringBuilder();
@ -181,6 +183,9 @@ namespace ILCompiler.DependencyAnalysis
}
EmitNativeMain();
EmitDebugMetadata();
LLVM.WriteBitcodeToFile(Module, _objectFilePath);
#if DEBUG
LLVM.PrintModuleToFile(Module, Path.ChangeExtension(_objectFilePath, ".txt"), out string unused2);
@ -190,6 +195,25 @@ namespace ILCompiler.DependencyAnalysis
//throw new NotImplementedException(); // This function isn't complete
}
private void EmitDebugMetadata()
{
var dwarfVersion = LLVM.MDNode(new[]
{
LLVM.ConstInt(LLVM.Int32Type(), 2, false),
LLVM.MDString("Dwarf Version", 13),
LLVM.ConstInt(LLVM.Int32Type(), 4, false)
});
var dwarfSchemaVersion = LLVM.MDNode(new[]
{
LLVM.ConstInt(LLVM.Int32Type(), 2, false),
LLVM.MDString("Debug Info Version", 18),
LLVM.ConstInt(LLVM.Int32Type(), 3, false)
});
LLVM.AddNamedMetadataOperand(Module, "llvm.module.flags", dwarfVersion);
LLVM.AddNamedMetadataOperand(Module, "llvm.module.flags", dwarfSchemaVersion);
LLVM.DIBuilderFinalize(DIBuilder);
}
public static LLVMValueRef GetConstZeroArray(int length)
{
var int8Type = LLVM.Int8Type();
@ -645,6 +669,7 @@ namespace ILCompiler.DependencyAnalysis
_nodeFactory = factory;
_objectFilePath = objectFilePath;
Module = compilation.Module;
DIBuilder = compilation.DIBuilder;
}
public void Dispose()

Просмотреть файл

@ -9,6 +9,7 @@ using Internal.TypeSystem;
using ILCompiler.DependencyAnalysis;
using ILCompiler.DependencyAnalysisFramework;
using LLVMSharp;
using ILCompiler.WebAssembly;
namespace ILCompiler
{
@ -17,18 +18,23 @@ namespace ILCompiler
internal WebAssemblyCodegenConfigProvider Options { get; }
internal LLVMModuleRef Module { get; }
public new WebAssemblyCodegenNodeFactory NodeFactory { get; }
internal LLVMDIBuilderRef DIBuilder { get; }
internal Dictionary<string, DebugMetadata> DebugMetadataMap { get; }
internal WebAssemblyCodegenCompilation(
DependencyAnalyzerBase<NodeFactory> dependencyGraph,
WebAssemblyCodegenNodeFactory nodeFactory,
IEnumerable<ICompilationRootProvider> roots,
DebugInformationProvider debugInformationProvider,
Logger logger,
WebAssemblyCodegenConfigProvider options)
: base(dependencyGraph, nodeFactory, GetCompilationRoots(roots, nodeFactory), null, null, logger)
: base(dependencyGraph, nodeFactory, GetCompilationRoots(roots, nodeFactory), debugInformationProvider, null, logger)
{
NodeFactory = nodeFactory;
Module = LLVM.ModuleCreateWithName("netscripten");
LLVM.SetTarget(Module, "asmjs-unknown-emscripten");
Options = options;
DIBuilder = LLVMPInvokes.LLVMCreateDIBuilder(Module);
DebugMetadataMap = new Dictionary<string, DebugMetadata>();
}
private static IEnumerable<ICompilationRootProvider> GetCompilationRoots(IEnumerable<ICompilationRootProvider> existingRoots, NodeFactory factory)

Просмотреть файл

@ -34,8 +34,7 @@ namespace ILCompiler
var interopStubManager = new CompilerGeneratedInteropStubManager(_compilationGroup, _context, new InteropStateManager(_context.GeneratedAssembly));
WebAssemblyCodegenNodeFactory factory = new WebAssemblyCodegenNodeFactory(_context, _compilationGroup, _metadataManager, interopStubManager, _nameMangler, _vtableSliceProvider, _dictionaryLayoutProvider);
DependencyAnalyzerBase<NodeFactory> graph = CreateDependencyGraph(factory, new ObjectNode.ObjectNodeComparer(new CompilerComparer()));
return new WebAssemblyCodegenCompilation(graph, factory, _compilationRoots, _logger, _config);
return new WebAssemblyCodegenCompilation(graph, factory, _compilationRoots, _debugInformationProvider, _logger, _config);
}
}

Просмотреть файл

@ -37,8 +37,10 @@
<Compile Include="..\..\Common\src\TypeSystem\IL\HelperExtensions.cs">
<Link>IL\HelperExtensions.cs</Link>
</Compile>
<Compile Include="CodeGen\DebugMetadata.cs" />
<Compile Include="CodeGen\ILToWebAssemblyImporter_Statics.cs" />
<Compile Include="CodeGen\LLVMMisc.cs" />
<Compile Include="CodeGen\LLVMPInvokes.cs" />
<Compile Include="CodeGen\WebAssemblyObjectWriter.cs" />
<Compile Include="Compiler\DependencyAnalysis\RawMainMethodRootProvider.cs" />
<Compile Include="Compiler\DependencyAnalysis\WebAssemblyCodegenNodeFactory.cs" />
@ -56,13 +58,13 @@
<Content Include="..\..\..\packages\llvmsharp\5.0.0\lib\netstandard1.1\LLVMSharp.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\..\..\packages\libllvm\4.0.0\runtimes\osx\native\libLLVM.dylib" Condition="'$(NuPkgRid)' == 'osx-x64'">
<Content Include="..\..\..\packages\libllvm\6.0.0-beta1\runtimes\osx\native\libLLVM.dylib" Condition="'$(NuPkgRid)' == 'osx-x64'">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\..\..\packages\libllvm\4.0.0\runtimes\linux-x64\native\libLLVM.so" Condition="'$(NuPkgRid)' == 'linux-x64'">
<Content Include="..\..\..\packages\libllvm\6.0.0-beta1\runtimes\linux-x64\native\libLLVM.so" Condition="'$(NuPkgRid)' == 'linux-x64'">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\..\..\packages\libllvm\4.0.0\runtimes\win-x64\native\libLLVM.dll" Condition="'$(NuPkgRid)' == 'win-x64' or '$(NuPkgRid)' == ''">
<Content Include="..\..\..\packages\libllvm\6.0.0-beta1\runtimes\win-x64\native\libLLVM.dll" Condition="'$(NuPkgRid)' == 'win-x64' or '$(NuPkgRid)' == ''">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

Просмотреть файл

@ -13,7 +13,7 @@
<ItemGroup>
<PackageReference Include="libLLVM">
<Version>4.0.0</Version>
<Version>6.0.0-beta1</Version>
</PackageReference>
</ItemGroup>