Beispiel #1
0
        public void WriteToFileTest( )
        {
            string path = Path.GetTempFileName( );

            try
            {
                using (var context = new Context( ))
                    using (var module = context.CreateBitcodeModule(TestModuleName))
                    {
                        /*Function testFunc =*/ CreateSimpleVoidNopTestFunction(module, "foo");
                        module.WriteToFile(path);
                    }

                using (var ctx = new Context( ))
                    using (var module2 = BitcodeModule.LoadFrom(path, ctx))
                    {
                        // force a GC to ensure buffer created in LoadFrom is handled correctly
                        GC.Collect(GC.MaxGeneration);
                        IrFunction testFunc = module2.GetFunction("foo");

                        // verify basics
                        Assert.IsNotNull(testFunc);
                        string txt = module2.WriteToString( );
                        Assert.IsFalse(string.IsNullOrWhiteSpace(txt));
                        string expectedText = string.Format(CultureInfo.InvariantCulture, TestModuleTemplate, Environment.NewLine, path);
                        Assert.AreEqual(expectedText, txt);
                    }
            }
            finally
            {
                File.Delete(path);
            }
        }
Beispiel #2
0
        private void AddDebugInfoForAlloca(Alloca argSlot, IrFunction function, LocalVariableDeclaration localVar)
        {
            uint line = ( uint )localVar.Location.StartLine;
            uint col  = ( uint )localVar.Location.StartColumn;

            // Keep compiler happy on null checks by asserting on expectations
            // The items were created in this file with all necessary info so
            // these properties should never be null.
            Debug.Assert(function.DISubProgram != null, "expected function with non-null DISubProgram");
            Debug.Assert(function.DISubProgram.File != null, "expected function with non-null DISubProgram.File");
            Debug.Assert(InstructionBuilder.InsertBlock != null, "expected Instruction builder with non-null insertion block");

            DILocalVariable debugVar = Module.DIBuilder.CreateLocalVariable(scope: function.DISubProgram
                                                                            , name: localVar.Name
                                                                            , file: function.DISubProgram.File
                                                                            , line
                                                                            , type: DoubleType
                                                                            , alwaysPreserve: false
                                                                            , debugFlags: DebugInfoFlags.None
                                                                            );

            Module.DIBuilder.InsertDeclare(storage: argSlot
                                           , varInfo: debugVar
                                           , location: new DILocation(Context, line, col, function.DISubProgram)
                                           , insertAtEnd: InstructionBuilder.InsertBlock
                                           );
        }
Beispiel #3
0
 public void AddABIAttributesForByValueStructure(IrFunction function, int paramIndex)
 {
     if (!(function.Parameters[paramIndex].NativeType is IPointerType argType) || !argType.ElementType.IsStruct)
     {
         throw new ArgumentException("Signature for specified parameter must be a pointer to a structure");
     }
 }
Beispiel #4
0
        private static BitcodeModule CreateModule(Context ctx, TargetMachine machine, int magicNumber, string modulename = "test", string functionname = "main")
        {
            var module = ctx.CreateBitcodeModule(modulename);

            module.Layout = machine.TargetData;
            IrFunction main       = module.CreateFunction(functionname, ctx.GetFunctionType(ctx.Int32Type));
            BasicBlock entryBlock = main.AppendBasicBlock("entry");
            var        bldr       = new InstructionBuilder(entryBlock);

            bldr.Return(ctx.CreateConstant(magicNumber));
            return(module);
        }
Beispiel #5
0
        public void VerifyInvalidModuleTest( )
        {
            using var context = new Context( );
            using var module  = context.CreateBitcodeModule(TestModuleName);
            IrFunction testFunc = CreateInvalidFunction(module, "badfunc");

            // verify basics
            Assert.IsNotNull(testFunc);
            bool isValid = module.Verify(out string msg);

            Assert.IsFalse(isValid);
            Assert.IsNotNull(msg);
        }
Beispiel #6
0
        public void VerifyValidModuleTest( )
        {
            using var context = new Context( );
            using var module  = context.CreateBitcodeModule(TestModuleName);
            IrFunction testFunc = CreateSimpleVoidNopTestFunction(module, "foo");

            // verify basics
            Assert.IsNotNull(testFunc);
            bool isValid = module.Verify(out string msg);

            Assert.IsTrue(isValid);
            Assert.AreEqual(string.Empty, msg);
        }
Beispiel #7
0
        private static void CreateDoCopyFunctionBody(BitcodeModule module
                                                     , IrFunction doCopyFunc
                                                     , IStructType foo
                                                     , GlobalVariable bar
                                                     , GlobalVariable baz
                                                     , IrFunction copyFunc
                                                     )
        {
            var bytePtrType = module.Context.Int8Type.CreatePointerType( );

            // create block for the function body, only need one for this simple sample
            var blk = doCopyFunc.AppendBasicBlock("entry");

            Debug.Assert(doCopyFunc.DISubProgram != null, "Expected non null subProgram");

            // create instruction builder to build the body
            var instBuilder = new InstructionBuilder(blk);

            bool param0ByVal = copyFunc.Attributes[FunctionAttributeIndex.Parameter0].Contains(AttributeKind.ByVal);

            if (!param0ByVal)
            {
                // create a temp local copy of the global structure
                var dstAddr = instBuilder.Alloca(foo)
                              .RegisterName("agg.tmp")
                              .Alignment(module.Layout.CallFrameAlignmentOf(foo));

                instBuilder.SetDebugLocation(25, 11, doCopyFunc.DISubProgram);
                var bitCastDst = instBuilder.BitCast(dstAddr, bytePtrType);
                var bitCastSrc = instBuilder.BitCast(bar, bytePtrType);

                instBuilder.MemCpy(bitCastDst
                                   , bitCastSrc
                                   , module.Context.CreateConstant(module.Layout.ByteSizeOf(foo))
                                   , false
                                   );

                instBuilder.SetDebugLocation(25, 5, doCopyFunc.DISubProgram)
                .Call(copyFunc, dstAddr, baz);
            }
            else
            {
                instBuilder.SetDebugLocation(25, 5, doCopyFunc.DISubProgram)
                .Call(copyFunc, bar, baz)
                .AddAttributes(FunctionAttributeIndex.Parameter0, copyFunc.Parameters[0].Attributes);
            }

            instBuilder.SetDebugLocation(26, 1, doCopyFunc.DISubProgram)
            .Return( );
        }
Beispiel #8
0
        public void AddFunctionGetFunctionTest( )
        {
            using var context = new Context( );
            using var module  = context.CreateBitcodeModule(TestModuleName);
            IrFunction testFunc = CreateSimpleVoidNopTestFunction(module, "foo");

            // verify basics
            Assert.IsNotNull(testFunc);
            Assert.AreSame(module, testFunc.ParentModule);
            Assert.AreEqual("foo", testFunc.Name);

            // Verify the function is in the module, and getting it retrieves the same instance
            Assert.IsTrue(module.TryGetFunction("foo", out IrFunction? funcFromModule));
            Assert.AreSame(testFunc, funcFromModule);
        }
Beispiel #9
0
        public void AsStringTest( )
        {
            using (var context = new Context( ))
                using (var module = context.CreateBitcodeModule(TestModuleName))
                {
                    IrFunction testFunc = CreateSimpleVoidNopTestFunction(module, "foo");

                    // verify basics
                    Assert.IsNotNull(testFunc);
                    string txt = module.WriteToString( );
                    Assert.IsFalse(string.IsNullOrWhiteSpace(txt));
                    string expectedText = string.Format(CultureInfo.InvariantCulture, TestModuleTemplate, Environment.NewLine, "test");
                    Assert.AreEqual(expectedText, txt);
                }
        }
Beispiel #10
0
        /// <summary>Looks up a function in the module by name</summary>
        /// <param name="name">Name of the function</param>
        /// <param name="function">The function or <see langword="default"/> if not found</param>
        /// <returns><see langword="true"/> if the function was found or <see langword="false"/> if not</returns>
        public bool TryGetFunction(string name, out IrFunction function)
        {
            ThrowIfDisposed( );

            var funcRef = ModuleHandle.GetNamedFunction(name);

            if (funcRef == default)
            {
                function = default;
                return(false);
            }

            function = Value.FromHandle <IrFunction>(funcRef) !;
            return(true);
        }
Beispiel #11
0
        public void AddFunctionGetFunctionTest( )
        {
            using (var context = new Context( ))
                using (var module = context.CreateBitcodeModule(TestModuleName))
                {
                    IrFunction testFunc = CreateSimpleVoidNopTestFunction(module, "foo");

                    // verify basics
                    Assert.IsNotNull(testFunc);
                    Assert.AreSame(module, testFunc.ParentModule);
                    Assert.AreEqual("foo", testFunc.Name);

                    // Verify the function is in the module
                    var funcFromModule = module.GetFunction("foo");
                    Assert.AreSame(testFunc, funcFromModule);
                }
        }
Beispiel #12
0
        public void VerifyInvalidModuleTest( )
        {
            using (var context = new Context( ))
                using (var module = context.CreateBitcodeModule(TestModuleName))
                {
                    IrFunction testFunc = CreateInvalidFunction(module, "badfunc");

                    // verify basics
                    Assert.IsNotNull(testFunc);
                    bool isValid = module.Verify(out string msg);
                    Assert.IsFalse(isValid);
                    Assert.IsNotNull(msg);

                    /* while verifying the contents of the message might be a normal test
                     * it comes from the underlying wrapped LLVM and is subject to change
                     * by the LLVM team and is therefore outside the control of LLVM.NET
                     */
                }
        }
        private void AddDebugInfoForAlloca(Alloca argSlot, IrFunction function, LocalVariableDeclaration localVar)
        {
            uint line = ( uint )localVar.Location.StartLine;
            uint col  = ( uint )localVar.Location.StartColumn;

            DILocalVariable debugVar = Module.DIBuilder.CreateLocalVariable(scope: function.DISubProgram
                                                                            , name: localVar.Name
                                                                            , file: function.DISubProgram.File
                                                                            , line
                                                                            , type: DoubleType
                                                                            , alwaysPreserve: false
                                                                            , debugFlags: DebugInfoFlags.None
                                                                            );

            Module.DIBuilder.InsertDeclare(storage: argSlot
                                           , varInfo: debugVar
                                           , location: new DILocation(Context, line, col, function.DISubProgram)
                                           , insertAtEnd: InstructionBuilder.InsertBlock
                                           );
        }
        private void AddDebugInfoForAlloca(Alloca argSlot, IrFunction function, ParameterDeclaration param)
        {
            uint line = ( uint )param.Location.StartLine;
            uint col  = ( uint )param.Location.StartColumn;

            DILocalVariable debugVar = Module.DIBuilder.CreateArgument(scope: function.DISubProgram
                                                                       , name: param.Name
                                                                       , file: function.DISubProgram.File
                                                                       , line
                                                                       , type: DoubleType
                                                                       , alwaysPreserve: true
                                                                       , debugFlags: DebugInfoFlags.None
                                                                       , argNo: checked (( ushort )(param.Index + 1)) // Debug index starts at 1!
                                                                       );

            Module.DIBuilder.InsertDeclare(storage: argSlot
                                           , varInfo: debugVar
                                           , location: new DILocation(Context, line, col, function.DISubProgram)
                                           , insertAtEnd: InstructionBuilder.InsertBlock
                                           );
        }
Beispiel #15
0
        public void AddAliasGetAliasTest( )
        {
            using var context = new Context( );
            using var module  = context.CreateBitcodeModule(TestModuleName);
            IrFunction testFunc = CreateSimpleVoidNopTestFunction(module, "_test");

            var alias = module.AddAlias(testFunc, TestModuleName);

            Assert.AreSame(alias, module.GetAlias(TestModuleName));
            Assert.AreSame(module, alias.ParentModule);
            Assert.AreSame(testFunc, alias.Aliasee);
            Assert.AreEqual(TestModuleName, alias.Name);
            Assert.AreEqual(Linkage.External, alias.Linkage);
            Assert.AreSame(testFunc.NativeType, alias.NativeType);

            Assert.AreEqual(1, alias.Operands.Count);
            Assert.AreSame(testFunc, alias.Aliasee);

            Assert.IsFalse(alias.IsNull);
            Assert.IsFalse(alias.IsUndefined);
            Assert.IsFalse(alias.IsZeroValue);
        }
        public void AddABIAttributesForByValueStructure(IrFunction function, int paramIndex)
        {
            // ByVal pointers indicate by value semantics. The actual LLVM semantics are along the lines of
            // "pass the arg as copy on the arguments stack and set parameter implicitly to that copy's address"
            // (src: https://github.com/ldc-developers/ldc/issues/937 ) [e.g. caller copies byval args]
            //
            // LLVM recognizes this pattern and has a pass to map to an efficient register usage whenever plausible.
            // Though it seems Clang doesn't apply the attribute in all cases, for x86 it doesn't appear to ever use
            // it, for Cortex-Mx it seems to use it only for larger structs, otherwise it uses an [ n x i32]. (Max
            // value of n is not known) and performs casts. Thus, on cortex-m the function parameters are handled
            // quite differently by clang, which seems odd to put such target dependent differences into the front-end.
            if (!(function.Parameters[paramIndex].NativeType is IPointerType argType) || !argType.ElementType.IsStruct)
            {
                throw new ArgumentException("Signature for specified parameter must be a pointer to a structure");
            }

            var layout = function.ParentModule.Layout;

            function.AddAttributes(FunctionAttributeIndex.Parameter0 + paramIndex
                                   , function.Context.CreateAttribute(AttributeKind.ByVal)
                                   , function.Context.CreateAttribute(AttributeKind.Alignment, layout.AbiAlignmentOf(argType.ElementType))
                                   );
        }
Beispiel #17
0
        void Compile(IrFunction func)
        {
            Write($"public static {From(func.ReturnType)} {func.Name}({string.Join(", ", func.ParameterTypes.Select((pt, i) => $"{From(pt)} _{i}"))}) {{");
            Indentation++;

            var blockPhiResolvers = new Dictionary <string, List <string> >();

            foreach (var block in func.Blocks)
            {
                blockPhiResolvers[block.Name] = new List <string>();
            }

            foreach (var block in func.Blocks)
            {
                foreach (var inst in block.Instructions)
                {
                    if (inst is PhiInst phi)
                    {
                        Write($"{From(phi.Output.Type)} {Rewrite(phi.Output)};");
                        foreach (var(bname, value) in phi.Incoming)
                        {
                            blockPhiResolvers[bname].Add($"{Rewrite(phi.Output)} = {Rewrite(value)};");
                        }
                    }
                }
            }

            foreach (var block in func.Blocks)
            {
                bool finished = false;
                void FinishBlock()
                {
                    if (finished)
                    {
                        return;
                    }
                    finished = true;
                    blockPhiResolvers[block.Name].ForEach(Write);
                }

                Write($"{RenameBlock(block.Name)}:");
                Indentation++;
                foreach (var inst in block.Instructions)
                {
                    switch (inst)
                    {
                    case AllocaInst a:
                        Write($"var {Rewrite(a.Output)} = stackalloc {From(a.AllocationType)}[{Rewrite(a.AllocationRank)}];");
                        break;

                    case BinaryInst b:
                        Write($"var {Rewrite(b.Output)} = {Rewrite(b.A)} {BinaryOps[b.Op]} {Rewrite(b.B)};");
                        break;

                    case BitcastInst bc:
                        Write($"var {Rewrite(bc.Output)} = ({From(bc.Output.Type)}) {Rewrite(bc.Value)};");
                        break;

                    case BrIfInst bri:
                        FinishBlock();
                        Write($"if({Rewrite(bri.Condition)}) goto {RenameBlock(bri.If)};");
                        Write($"else goto {RenameBlock(bri.Else)};");
                        break;

                    case BrInst br:
                        FinishBlock();
                        Write($"goto {RenameBlock(br.Target)};");
                        break;

                    case CallInst call:
                        Write($"var {Rewrite(call.Output)} = {call.Target}({string.Join(", ", call.Parameters.Select(Rewrite))});");
                        break;

                    case IcmpInst icmp:
                        Write($"var {Rewrite(icmp.Output)} = {Rewrite(icmp.A)} {CompareOps[icmp.Predicate]} {Rewrite(icmp.B)};");
                        break;

                    case LoadInst load:
                        Write($"var {Rewrite(load.Output)} = *{Rewrite(load.Pointer)};");
                        break;

                    case PhiInst phi:
                        break;

                    case ReturnInst ret:
                        FinishBlock();
                        Write(ret.Value == null ? "return;" : $"return {Rewrite(ret.Value)};");
                        break;

                    case SelectInst select:
                        Write($"var {Rewrite(select.Output)} = {Rewrite(select.Compare)} ? {Rewrite(select.A)} : {Rewrite(select.B)};");
                        break;

                    case StoreInst store:
                        Write($"*{Rewrite(store.Pointer)} = {Rewrite(store.Value)};");
                        break;

                    default:
                        Console.Write("Unknown instruction: ");
                        inst.Print();
                        break;
                    }
                }
                FinishBlock();
                Indentation--;
            }

            Indentation--;
            Write("}");
        }
Beispiel #18
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");
            }
        }
 public bool Describes(IrFunction function)
 => LibLLVMSubProgramDescribes(MetadataHandle, function.ValidateNotNull(nameof(function)).ValueHandle);
Beispiel #20
0
        private static void CreateCopyFunctionBody(BitcodeModule module
                                                   , IrFunction copyFunc
                                                   , DIFile diFile
                                                   , ITypeRef foo
                                                   , DebugPointerType fooPtr
                                                   , DIType constFooType
                                                   )
        {
            Debug.Assert(copyFunc.DISubProgram != null, "Expected function with a valid debug subprogram");
            var diBuilder = module.DIBuilder;

            copyFunc.Parameters[0].Name = "src";
            copyFunc.Parameters[1].Name = "pDst";

            // create block for the function body, only need one for this simple sample
            var blk = copyFunc.AppendBasicBlock("entry");

            // create instruction builder to build the body
            var instBuilder = new InstructionBuilder(blk);

            // create debug info locals for the arguments
            // NOTE: Debug parameter indices are 1 based!
            var paramSrc = diBuilder.CreateArgument(copyFunc.DISubProgram, "src", diFile, 11, constFooType, false, 0, 1);
            var paramDst = diBuilder.CreateArgument(copyFunc.DISubProgram, "pDst", diFile, 12, fooPtr.DIType, false, 0, 2);

            uint ptrAlign = module.Layout.CallFrameAlignmentOf(fooPtr);

            // create Locals
            // NOTE: There's no debug location attached to these instructions.
            //       The debug info will come from the declare intrinsic below.
            var dstAddr = instBuilder.Alloca(fooPtr)
                          .RegisterName("pDst.addr")
                          .Alignment(ptrAlign);

            bool param0ByVal = copyFunc.Attributes[FunctionAttributeIndex.Parameter0].Contains(AttributeKind.ByVal);

            if (param0ByVal)
            {
                diBuilder.InsertDeclare(copyFunc.Parameters[0]
                                        , paramSrc
                                        , new DILocation(module.Context, 11, 43, copyFunc.DISubProgram)
                                        , blk
                                        );
            }

            instBuilder.Store(copyFunc.Parameters[1], dstAddr)
            .Alignment(ptrAlign);

            // insert declare pseudo instruction to attach debug info to the local declarations
            diBuilder.InsertDeclare(dstAddr, paramDst, new DILocation(module.Context, 12, 38, copyFunc.DISubProgram), blk);

            if (!param0ByVal)
            {
                // since the function's LLVM signature uses a pointer, which is copied locally
                // inform the debugger to treat it as the value by dereferencing the pointer
                diBuilder.InsertDeclare(copyFunc.Parameters[0]
                                        , paramSrc
                                        , diBuilder.CreateExpression(ExpressionOp.Deref)
                                        , new DILocation(module.Context, 11, 43, copyFunc.DISubProgram)
                                        , blk
                                        );
            }

            var loadedDst = instBuilder.SetDebugLocation(15, 6, copyFunc.DISubProgram)
                            .Load(fooPtr, dstAddr)
                            .Alignment(ptrAlign);

            instBuilder.SetDebugLocation(15, 13, copyFunc.DISubProgram);
            var dstPtr = instBuilder.BitCast(loadedDst, module.Context.Int8Type.CreatePointerType( ));
            var srcPtr = instBuilder.BitCast(copyFunc.Parameters[0], module.Context.Int8Type.CreatePointerType( ));

            uint pointerSize = module.Layout.IntPtrType(module.Context).IntegerBitWidth;

            instBuilder.MemCpy(dstPtr
                               , srcPtr
                               , module.Context.CreateConstant(pointerSize, module.Layout.ByteSizeOf(foo), false)
                               , false
                               );
            instBuilder.SetDebugLocation(16, 1, copyFunc.DISubProgram)
            .Return( );
        }