diff options
Diffstat (limited to 'src/ILCompiler.WebAssembly')
8 files changed, 246 insertions, 10 deletions
diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/DebugMetadata.cs b/src/ILCompiler.WebAssembly/src/CodeGen/DebugMetadata.cs new file mode 100644 index 000000000..30780db65 --- /dev/null +++ b/src/ILCompiler.WebAssembly/src/CodeGen/DebugMetadata.cs @@ -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; } + } +} diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs index 19960e6a9..5dd76b0fe 100644 --- a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs +++ b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs @@ -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() diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/LLVMPInvokes.cs b/src/ILCompiler.WebAssembly/src/CodeGen/LLVMPInvokes.cs new file mode 100644 index 000000000..708ef5b51 --- /dev/null +++ b/src/ILCompiler.WebAssembly/src/CodeGen/LLVMPInvokes.cs @@ -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, + } +} diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs b/src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs index 5b819d8a6..f89f1ce97 100644 --- a/src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs +++ b/src/ILCompiler.WebAssembly/src/CodeGen/WebAssemblyObjectWriter.cs @@ -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() diff --git a/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilation.cs b/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilation.cs index 5a32248a7..e49cbba16 100644 --- a/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilation.cs +++ b/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilation.cs @@ -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) diff --git a/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilationBuilder.cs b/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilationBuilder.cs index c65371259..3b40a60f7 100644 --- a/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilationBuilder.cs +++ b/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilationBuilder.cs @@ -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); } } diff --git a/src/ILCompiler.WebAssembly/src/ILCompiler.WebAssembly.csproj b/src/ILCompiler.WebAssembly/src/ILCompiler.WebAssembly.csproj index 58b4c14eb..26e9f3253 100644 --- a/src/ILCompiler.WebAssembly/src/ILCompiler.WebAssembly.csproj +++ b/src/ILCompiler.WebAssembly/src/ILCompiler.WebAssembly.csproj @@ -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> diff --git a/src/ILCompiler.WebAssembly/src/libLLVMdep.depproj b/src/ILCompiler.WebAssembly/src/libLLVMdep.depproj index 5c1bac3da..97b7dd4b9 100644 --- a/src/ILCompiler.WebAssembly/src/libLLVMdep.depproj +++ b/src/ILCompiler.WebAssembly/src/libLLVMdep.depproj @@ -13,7 +13,7 @@ <ItemGroup> <PackageReference Include="libLLVM"> - <Version>4.0.0</Version> + <Version>6.0.0-beta1</Version> </PackageReference> </ItemGroup> |