Ejemplo n.º 1
0
        public OptionalValue <BitcodeModule> Generate(IAstNode ast)
        {
            ast.ValidateNotNull(nameof(ast));
            ast.Accept(this);

            if (AnonymousFunctions.Count > 0)
            {
                var mainFunction = Module.CreateFunction("main", Context.GetFunctionType(Context.VoidType));
                var block        = mainFunction.AppendBasicBlock("entry");
                var irBuilder    = new InstructionBuilder(block);
                var printdFunc   = Module.CreateFunction("printd", Context.GetFunctionType(Context.DoubleType, Context.DoubleType));
                foreach (var anonFunc in AnonymousFunctions)
                {
                    var value = irBuilder.Call(anonFunc);
                    irBuilder.Call(printdFunc, value);
                }

                irBuilder.Return( );

                // Use always inline and Dead Code Elimination module passes to inline all of the
                // anonymous functions. This effectively strips all the calls just generated for main()
                // and inlines each of the anonymous functions directly into main, dropping the now
                // unused original anonymous functions all while retaining all of the original source
                // debug information locations.
                using var mpm = new ModulePassManager( );
                mpm.AddAlwaysInlinerPass( )
                .AddGlobalDCEPass( )
                .Run(Module);

                Module.DIBuilder.Finish( );
            }

            return(OptionalValue.Create(Module));
        }
Ejemplo n.º 2
0
        public Value Generate(IAstNode ast, Action <CodeGeneratorException> codeGenerationErroHandler)
        {
            try
            {
                ast.Accept(this);

                if (AnonymousFunctions.Count > 0)
                {
                    var mainFunction = Module.AddFunction("main", Context.GetFunctionType(Context.VoidType));
                    var block        = mainFunction.AppendBasicBlock("entry");
                    var irBuilder    = new InstructionBuilder(block);
                    var printdFunc   = Module.AddFunction("printd", Context.GetFunctionType(Context.DoubleType, Context.DoubleType));
                    foreach (var anonFunc in AnonymousFunctions)
                    {
                        var value = irBuilder.Call(anonFunc);
                        irBuilder.Call(printdFunc, value);
                    }

                    irBuilder.Return( );

                    // Use always inline and Dead Code Elimination module passes to inline all of the
                    // anonymous functions. This effectively strips all the calls just generated for main()
                    // and inlines each of the anonymous functions directly into main, dropping the now
                    // unused original anonymous functions all while retaining all of the original source
                    // debug information locations.
                    var mpm = new ModulePassManager( )
                              .AddAlwaysInlinerPass( )
                              .AddGlobalDCEPass( );
                    mpm.Run(Module);
                    Module.DIBuilder.Finish( );
                }
            }
            catch (CodeGeneratorException ex) when(codeGenerationErroHandler != null)
            {
                codeGenerationErroHandler(ex);
            }

            return(null);
        }
Ejemplo n.º 3
0
        /// <summary>Creates a test LLVM module with debug information</summary>
        /// <param name="args">ignored</param>
        /// <remarks>
        /// <code language="c" title="Example code generated" source="test.c" />
        /// </remarks>
        public static void Main(string[] args)
        {
            TargetDetails = new CortexM3Details();
            string srcPath = args[0];

            if (!File.Exists(srcPath))
            {
                Console.Error.WriteLine("Src file not found: '{0}'", srcPath);
                return;
            }

            srcPath = Path.GetFullPath(srcPath);

            string moduleName = $"test_{TargetDetails.ShortName}.bc";

            using (StaticState.InitializeLLVM())
            {
                StaticState.RegisterAll( );
                var target = Target.FromTriple(TargetDetails.Triple);
                using (var context = new Context( ))
                    using (var targetMachine = target.CreateTargetMachine(TargetDetails.Triple, TargetDetails.Cpu, TargetDetails.Features, CodeGenOpt.Aggressive, Reloc.Default, CodeModel.Small))
                        using (var module = new BitcodeModule(context, moduleName))
                        {
                            module.SourceFileName     = Path.GetFileName(srcPath);
                            TargetDependentAttributes = TargetDetails.BuildTargetDependentFunctionAttributes(context);
                            var targetData = targetMachine.TargetData;

                            module.TargetTriple = targetMachine.Triple;
                            module.Layout       = targetMachine.TargetData;

                            // create compile unit and file as the top level scope for everything
                            var cu = module.DIBuilder.CreateCompileUnit(SourceLanguage.C99
                                                                        , Path.GetFileName(srcPath)
                                                                        , Path.GetDirectoryName(srcPath)
                                                                        , VersionIdentString
                                                                        , false
                                                                        , string.Empty
                                                                        , 0
                                                                        );
                            var diFile = module.DIBuilder.CreateFile(srcPath);

                            // Create basic types used in this compilation
                            var i32           = new DebugBasicType(module.Context.Int32Type, module, "int", DiTypeKind.Signed);
                            var f32           = new DebugBasicType(module.Context.FloatType, module, "float", DiTypeKind.Float);
                            var voidType      = DebugType.Create(module.Context.VoidType, ( DIType )null);
                            var i32Array_0_32 = i32.CreateArrayType(module, 0, 32);

                            // create the LLVM structure type and body with full debug information
    #pragma warning disable SA1500 // "Warning SA1500  Braces for multi - line statements must not share line" (simple table format)
                            var fooBody = new[]
                            { new DebugMemberInfo {
                                  File = diFile, Line = 3, Name = "a", DebugType = i32, Index = 0
                              }
                              , new DebugMemberInfo {
                                  File = diFile, Line = 4, Name = "b", DebugType = f32, Index = 1
                              }
                              , new DebugMemberInfo {
                                  File = diFile, Line = 5, Name = "c", DebugType = i32Array_0_32, Index = 2
                              } };
    #pragma warning restore
                            var fooType = new DebugStructType(module, "struct.foo", cu, "foo", diFile, 1, DebugInfoFlags.None, fooBody);

                            // add global variables and constants
                            var constArray = ConstantArray.From(i32, 32, module.Context.CreateConstant(3), module.Context.CreateConstant(4));
                            var barValue   = module.Context.CreateNamedConstantStruct(fooType
                                                                                      , module.Context.CreateConstant(1)
                                                                                      , module.Context.CreateConstant(2.0f)
                                                                                      , constArray
                                                                                      );

                            var bar = module.AddGlobal(fooType, false, 0, barValue, "bar");
                            bar.Alignment = targetData.AbiAlignmentOf(fooType);
                            bar.AddDebugInfo(module.DIBuilder.CreateGlobalVariableExpression(cu, "bar", string.Empty, diFile, 8, fooType.DIType, false, null));

                            var baz = module.AddGlobal(fooType, false, Linkage.Common, Constant.NullValueFor(fooType), "baz");
                            baz.Alignment = targetData.AbiAlignmentOf(fooType);
                            baz.AddDebugInfo(module.DIBuilder.CreateGlobalVariableExpression(cu, "baz", string.Empty, diFile, 9, fooType.DIType, false, null));

                            // add module flags and compiler identifiers...
                            // this can technically occur at any point, though placing it here makes
                            // comparing against clang generated files easier
                            AddModuleFlags(module);

                            // create types for function args
                            var constFoo = module.DIBuilder.CreateQualifiedType(fooType.DIType, QualifiedTypeTag.Const);
                            var fooPtr   = new DebugPointerType(fooType, module);

                            // Create the functions
                            // NOTE: The declaration ordering is reversed from that of the sample code file (test.c)
                            //       However, this is what Clang ends up doing for some reason so it is
                            //       replicated here to aid in comparing the generated LL files.
                            Function doCopyFunc = DeclareDoCopyFunc(module, diFile, voidType);
                            Function copyFunc   = DeclareCopyFunc(module, diFile, voidType, constFoo, fooPtr);

                            CreateCopyFunctionBody(module, targetData, copyFunc, diFile, fooType, fooPtr, constFoo);
                            CreateDoCopyFunctionBody(module, targetData, doCopyFunc, fooType, bar, baz, copyFunc);

                            // finalize the debug information
                            // all temporaries must be replaced by now, this resolves any remaining
                            // forward declarations and marks the builder to prevent adding any
                            // nodes that are not completely resolved.
                            module.DIBuilder.Finish( );

                            // verify the module is still good and print any errors found
                            if (!module.Verify(out string msg))
                            {
                                Console.Error.WriteLine("ERROR: {0}", msg);
                            }
                            else
                            {
                                // test optimization works, but don't save it as that makes it harder to do a diff with official clang builds
                                {// force a GC to verify callback delegate for diagnostics is still valid, this is for test only and wouldn't
                                 // normally be done in production code.
                                    GC.Collect(GC.MaxGeneration);
                                    var modForOpt = module.Clone( );

                                    // NOTE:
                                    // The ordering of passes can matter depending on the pass and passes may be added more than once
                                    // the caller has full control of ordering, this is just a sample of effectively randomly picked
                                    // passes and not necessarily a reflection of any particular use case.
                                    var pm = new ModulePassManager( )
                                             .AddAlwaysInlinerPass( )
                                             .AddAggressiveDCEPass( )
                                             .AddArgumentPromotionPass( )
                                             .AddBasicAliasAnalysisPass( )
                                             .AddBitTrackingDCEPass( )
                                             .AddCFGSimplificationPass( )
                                             .AddConstantMergePass( )
                                             .AddConstantPropagationPass( )
                                             .AddFunctionInliningPass( )
                                             .AddGlobalOptimizerPass( )
                                             .AddInstructionCombiningPass( );

                                    pm.Run(modForOpt);
                                }

                                // Module is good, so generate the output files
                                module.WriteToFile("test.bc");
                                File.WriteAllText("test.ll", module.WriteToString( ));
                                targetMachine.EmitToFile(module, "test.o", CodeGenFileType.ObjectFile);
                                targetMachine.EmitToFile(module, "test.s", CodeGenFileType.AssemblySource);
                            }
                        }
            }
        }
Ejemplo n.º 4
0
        public static void Main(string[] args)
        {
            #region CommandlineArguments
            if (args.Length < 2 || args.Length > 3)
            {
                ShowUsage( );
                return;
            }

            string outputPath = args.Length == 3 ? args[2] : Environment.CurrentDirectory;

            string srcPath = args[1];
            if (!File.Exists(srcPath))
            {
                Console.Error.WriteLine("Src file not found: '{0}'", srcPath);
                return;
            }

            srcPath = Path.GetFullPath(srcPath);
            #endregion

            using var libLLVM = InitializeLLVM( );

            #region TargetDetailsSelection
            switch (args[0].ToUpperInvariant( ))
            {
            case "M3":
                TargetDetails = new CortexM3Details(libLLVM);
                break;

            case "X64":
                TargetDetails = new X64Details(libLLVM);
                break;

            default:
                ShowUsage( );
                return;
            }

            string moduleName = $"test_{TargetDetails.ShortName}.bc";
            #endregion

            #region CreatingModule
            using var context     = new Context( );
            using var module      = context.CreateBitcodeModule(moduleName, SourceLanguage.C99, srcPath, VersionIdentString);
            module.SourceFileName = Path.GetFileName(srcPath);
            module.TargetTriple   = TargetDetails.TargetMachine.Triple;
            module.Layout         = TargetDetails.TargetMachine.TargetData;
            Debug.Assert(!(module.DICompileUnit is null), "Expected module with non-null compile unit");

            TargetDependentAttributes = TargetDetails.BuildTargetDependentFunctionAttributes(context);
            #endregion

            var diFile = module.DIBuilder.CreateFile(srcPath);

            #region CreatingBasicTypesWithDebugInfo
            // Create basic types used in this compilation
            var i32           = new DebugBasicType(module.Context.Int32Type, module, "int", DiTypeKind.Signed);
            var f32           = new DebugBasicType(module.Context.FloatType, module, "float", DiTypeKind.Float);
            var voidType      = DebugType.Create <ITypeRef, DIType>(module.Context.VoidType, null);
            var i32Array_0_32 = i32.CreateArrayType(module, 0, 32);
            #endregion

            #region CreatingStructureTypes
            // create the LLVM structure type and body with full debug information
            var fooBody = new[]
            {
                new DebugMemberInfo(0, "a", diFile, 3, i32),
                new DebugMemberInfo(1, "b", diFile, 4, f32),
                new DebugMemberInfo(2, "c", diFile, 5, i32Array_0_32),
            };

            var fooType = new DebugStructType(module, "struct.foo", module.DICompileUnit, "foo", diFile, 1, DebugInfoFlags.None, fooBody);
            #endregion

            #region CreatingGlobalsAndMetadata
            // add global variables and constants
            var constArray = ConstantArray.From(i32, 32, module.Context.CreateConstant(3), module.Context.CreateConstant(4));
            var barValue   = module.Context.CreateNamedConstantStruct(fooType
                                                                      , module.Context.CreateConstant(1)
                                                                      , module.Context.CreateConstant(2.0f)
                                                                      , constArray
                                                                      );

            var bar = module.AddGlobal(fooType, false, 0, barValue, "bar");
            bar.Alignment = module.Layout.AbiAlignmentOf(fooType);
            bar.AddDebugInfo(module.DIBuilder.CreateGlobalVariableExpression(module.DICompileUnit, "bar", string.Empty, diFile, 8, fooType.DIType, false, null));

            var baz = module.AddGlobal(fooType, false, Linkage.Common, Constant.NullValueFor(fooType), "baz");
            baz.Alignment = module.Layout.AbiAlignmentOf(fooType);
            baz.AddDebugInfo(module.DIBuilder.CreateGlobalVariableExpression(module.DICompileUnit, "baz", string.Empty, diFile, 9, fooType.DIType, false, null));

            // add module flags and compiler identifiers...
            // this can technically occur at any point, though placing it here makes
            // comparing against clang generated files easier
            AddModuleFlags(module);
            #endregion

            #region CreatingQualifiedTypes
            // create types for function args
            var constFoo = module.DIBuilder.CreateQualifiedType(fooType.DIType, QualifiedTypeTag.Const);
            var fooPtr   = new DebugPointerType(fooType, module);
            #endregion

            // Create the functions
            // NOTE: The declaration ordering is reversed from that of the sample code file (test.c)
            //       However, this is what Clang ends up doing for some reason so it is
            //       replicated here to aid in comparing the generated LL files.
            IrFunction doCopyFunc = DeclareDoCopyFunc(module, diFile, voidType);
            IrFunction copyFunc   = DeclareCopyFunc(module, diFile, voidType, constFoo, fooPtr);

            CreateCopyFunctionBody(module, copyFunc, diFile, fooType, fooPtr, constFoo);
            CreateDoCopyFunctionBody(module, doCopyFunc, fooType, bar, baz, copyFunc);

            // finalize the debug information
            // all temporaries must be replaced by now, this resolves any remaining
            // forward declarations and marks the builder to prevent adding any
            // nodes that are not completely resolved.
            module.DIBuilder.Finish( );

            // verify the module is still good and print any errors found
            if (!module.Verify(out string msg))
            {
                Console.Error.WriteLine("ERROR: {0}", msg);
            }
            else
            {
                // test optimization works, but don't save it as that makes it harder to do a compare with official clang builds
                {// force a GC to verify callback delegate for diagnostics is still valid, this is for test only and wouldn't
                    // normally be done in production code.
                    GC.Collect(GC.MaxGeneration);
                    using var modForOpt = module.Clone( );
                    // NOTE:
                    // The ordering of passes can matter depending on the pass, and passes may be added more than once
                    // the caller has full control of ordering, this is just a sample of effectively randomly picked
                    // passes and not necessarily a reflection of any particular use case.
                    using var pm = new ModulePassManager( );
                    pm.AddAlwaysInlinerPass( )
                    .AddAggressiveDCEPass( )
                    .AddArgumentPromotionPass( )
                    .AddBasicAliasAnalysisPass( )
                    .AddBitTrackingDCEPass( )
                    .AddCFGSimplificationPass( )
                    .AddConstantMergePass( )
                    .AddConstantPropagationPass( )
                    .AddFunctionInliningPass( )
                    .AddGlobalOptimizerPass( )
                    .AddInstructionCombiningPass( )
                    .Run(modForOpt);
                }

                // Module is good, so generate the output files
                module.WriteToFile(Path.Combine(outputPath, "test.bc"));
                File.WriteAllText(Path.Combine(outputPath, "test.ll"), module.WriteToString( ));
                TargetDetails.TargetMachine.EmitToFile(module, Path.Combine(outputPath, "test.o"), CodeGenFileType.ObjectFile);
                TargetDetails.TargetMachine.EmitToFile(module, Path.Combine(outputPath, "test.s"), CodeGenFileType.AssemblySource);
                Console.WriteLine("Generated test.bc, test.ll, test.o, and test.s");
            }
        }
Ejemplo n.º 5
0
        public static Module Generate(Context context, string path, SemanticModule module,
                                      SemanticContext semanticContext, string targetTriple, PassManagerBuilder?optBuilder, bool debugInfo,
                                      bool columnInfo)
        {
            // Path information for debug info nodes
            string fileName = Path.GetFileName(path);
            string dirName  = Path.GetDirectoryName(path);

            if (dirName == null)
            {
                dirName = "/";
            }
            else if (dirName.Length == 0)
            {
                dirName = ".";
            }

            // Create module and file-level debug info nodes
            using CodeGeneratorContext genContext = new CodeGeneratorContext(context, module, semanticContext, fileName,
                                                                             dirName, targetTriple, optBuilder != null, debugInfo, columnInfo);

            // Enum pass
            foreach (EnumNode enumNode in genContext.ParseModule.Enums)
            {
                Metadata[] enumerators = new Metadata[enumNode.Declarations.Count];
                for (int i = 0, ilen = enumNode.Declarations.Count; i < ilen; ++i)
                {
                    var enumeration = enumNode.Declarations[i];
                    enumerators[i] = genContext.DiBuilder.CreateEnumerator(enumeration.Name, i, false);
                }

                genContext.TryGetNodeSymbol(enumNode, out Symbol range);
                genContext.DiBuilder.CreateEnumerationType(genContext.DiFile,
                                                           (enumNode.IsExported ? "export." : "") + $"enum.{fileName}.{range.LLVMLine}", genContext.DiFile,
                                                           range.LLVMLine, genContext.DiBuilder.Int32Type.GetTypeSizeInBits(),
                                                           genContext.DiBuilder.Int32Type.GetTypeAlignInBits(), enumerators, genContext.DiBuilder.Int32Type);
            }

            // Struct pass
            foreach (StructNode structNode in genContext.ParseModule.Structs)
            {
                genContext.EnsureStruct(structNode);
            }

            // Declaration pass
            foreach (FunctionDefinitionNode function in genContext.ParseModule.Functions)
            {
                genContext.RegisterDefinedFunction(function);
            }

            // Definition pass
            foreach (FunctionDefinitionNode function in genContext.ParseModule.Functions)
            {
                CodeGeneratorContext.Function ctxFunction = genContext.GetFunctionDefinition(function);

                using Builder builder = genContext.Context.CreateBuilder();
                FunctionCodeGenVisitor functionCodeGenVisitor = new FunctionCodeGenVisitor(genContext, ctxFunction,
                                                                                           builder, ctxFunction.StartDefinition(genContext, builder));
                builder.PositionAtEnd(functionCodeGenVisitor._basicBlock);
                function.Body.VisitStatements(functionCodeGenVisitor);

                if (genContext.DebugInfo)
                {
                    // TODO: Use the body end rather than the function end
                    genContext.TryGetNodeSymbol(function, out Symbol range);
                    Metadata location = genContext.Context.CreateDebugLocation(range.End.Line + 1,
                                                                               genContext.ColumnInfo ? range.End.Column + 1 : 0, ctxFunction.DiFunctionDef,
                                                                               Metadata.Null);
                    builder.SetCurrentDebugLocation(location);
                }

                // If we still have a valid insert block, this function did not end with a return; Insert one now
                if (builder.InsertBlock.IsValid)
                {
                    if (ctxFunction.ReturnBlock != null)
                    {
                        builder.BuildBr(ctxFunction.ReturnBlock.Value);
                    }
                    else
                    {
                        builder.BuildRetVoid();
                    }
                }

                if (ctxFunction.ReturnBlock != null && ctxFunction.RetvalStorage != null)
                {
                    ctxFunction.FunctionValue.AppendExistingBasicBlock(ctxFunction.ReturnBlock.Value);
                    builder.PositionAtEnd(ctxFunction.ReturnBlock.Value);
                    Value retVal = builder.BuildLoad(ctxFunction.RetvalStorage.Value);
                    builder.BuildRet(retVal);
                }
            }

            // Remove unused metadata nodes for undefined functions
            genContext.FinalizeFunctions();

            // Finalize debug info
            genContext.DiBuilder.BuilderFinalize();

            // Run optimization passes on functions and module if a builder is supplied
            if (optBuilder != null)
            {
                using ModulePassManager modulePassManager     = new ModulePassManager(optBuilder);
                using FunctionPassManager functionPassManager = new FunctionPassManager(genContext.Module, optBuilder);

                functionPassManager.Initialize();
                foreach (var function in genContext.DefinedFunctions)
                {
                    functionPassManager.Run(function.Value.FunctionValue);
                }

                functionPassManager.FinalizeFunctionPassManager();

                modulePassManager.Run(genContext.Module);
            }

            // Done with everything in CodeGeneratorContext besides the Module
            return(genContext.Module);
        }