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)); }
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); }
/// <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); } } } }
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"); } }
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); }