dotnet / LLVMSharp

LLVM bindings for .NET Standard written in C# using ClangSharp
MIT License
844 stars 97 forks source link

HelloWorld example implementation #199

Closed CWKSC closed 1 year ago

CWKSC commented 2 years ago

I am an LLVM beginner and trying to transform LLVM C++ API to LLVMSharp in my project (suffering from C++ and CMake)

So I am implementing a HelloWorld example with LLVMSharp for learning, but there are some things wrong

I create a console program with .NET 7 by Visual Studio 2022 Preview

I install LLVMSharp and libLLVM by NuGet, and add <RuntimeIdentifier Condition="'$(RuntimeIdentifier)' == ''">$(NETCoreSdkRuntimeIdentifier)</RuntimeIdentifier> into csproj to solve DllNotFoundException (https://github.com/dotnet/LLVMSharp/issues/157#issuecomment-779942328)

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <RuntimeIdentifier Condition="'$(RuntimeIdentifier)' == ''">$(NETCoreSdkRuntimeIdentifier)</RuntimeIdentifier>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="libLLVM" Version="15.0.0" />
    <PackageReference Include="LLVMSharp" Version="5.0.0" />
  </ItemGroup>

</Project>

With abstruct some utilities function like CreateExtern, DeclarePuts, CreateFunction, CreateMain ...

I create the following program:

using LLVMSharp;

LLVMValueRef CreateExtern(
    LLVMModuleRef module,
    string name,
    LLVMTypeRef returnType,
    LLVMTypeRef[] parameters,
    bool isVarArg = false
)
{
    var functionType = LLVM.FunctionType(returnType, parameters, isVarArg);
    return LLVM.AddFunction(module, name, functionType);
}

LLVMValueRef DeclarePuts(LLVMModuleRef module)
{
    LLVMTypeRef returnType = LLVM.Int32Type();
    LLVMTypeRef[] parameters = { LLVM.PointerType(LLVM.Int8Type(), 0) };
    return CreateExtern(module, "puts", returnType, parameters);
}

(LLVMValueRef, LLVMBuilderRef) CreateFunction(
    LLVMModuleRef module,
    string name,
    LLVMTypeRef returnType,
    LLVMTypeRef[] parameters,
    bool isVarArgs = false
)
{
    var funcType = LLVM.FunctionType(returnType, parameters, isVarArgs);
    var func = LLVM.AddFunction(module, name, funcType);
    var entry = LLVM.AppendBasicBlock(func, "entry");
    var builder = LLVM.CreateBuilder();
    LLVM.PositionBuilderAtEnd(builder, entry);

    return (func, builder);
}

(LLVMValueRef, LLVMBuilderRef) CreateMain(LLVMModuleRef module)
{
    return CreateFunction(module, "main", LLVM.Int32Type(), Array.Empty<LLVMTypeRef>());
}

void RunMain(LLVMModuleRef module)
{
    LLVM.LinkInMCJIT();
    LLVM.InitializeX86TargetMC();
    LLVM.InitializeX86Target();
    LLVM.InitializeX86TargetInfo();
    LLVM.InitializeX86AsmParser();
    LLVM.InitializeX86AsmPrinter();

    LLVM.CreateExecutionEngineForModule(out var engine, module, out var _);
    var main = LLVM.GetNamedFunction(module, "main");
    LLVM.RunFunction(engine, main, Array.Empty<LLVMGenericValueRef>());
}

var context = LLVM.ContextCreate();
var module = LLVM.ModuleCreateWithNameInContext("Hello World", context);

var funcPuts = DeclarePuts(module);
var (_, builder_main) = CreateMain(module);

var constInt0 = LLVM.ConstInt(LLVM.Int32Type(), 0, true);
var constStr = LLVM.BuildGlobalStringPtr(builder_main, "Hello World!", "");
var parameters = new LLVMValueRef[] { constStr };
LLVM.BuildCall(builder_main, funcPuts, parameters, "");
LLVM.BuildRet(builder_main, constInt0);

LLVM.DumpModule(module);

RunMain(module);

Program crashes in LLVM.BuildCall(builder_main, funcPuts, parameters, "");, and the console output:

Fatal error. System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Repeat 2 times:
--------------------------------
   at LLVMSharp.LLVM.BuildCall(LLVMSharp.LLVMBuilderRef, LLVMSharp.LLVMValueRef, LLVMSharp.LLVMValueRef ByRef, UInt32, System.String)
--------------------------------
   at LLVMSharp.LLVM.BuildCall(LLVMSharp.LLVMBuilderRef, LLVMSharp.LLVMValueRef, LLVMSharp.LLVMValueRef[], System.String)
   at LearnLLVMSharp.Program.Main(System.String[])

By LLVM.DumpModule(module);, the halfway result before the crash is as follows:

; ModuleID = 'Hello World'
source_filename = "Hello World"

@0 = private unnamed_addr constant [13 x i8] c"Hello World!\00", align 1

declare i32 @puts(ptr %0)

define i32 @main() {
entry:
}
CWKSC commented 2 years ago

If I remove libLLVM and only install LLVMSharp, the error disappear

CWKSC commented 2 years ago

I fix with removing libLLVM 15.0.0 and only installing LLVMSharp.

The program looks like using libLLVM 5.0.0, It is not normal, so I reopen it.

I guess the problem is related to the creation of a const global string.

CWKSC commented 2 years ago

I downgrade until libLLVM 14.0.0 and its work

By check out LLVM 15.0.0 ReleaseNotes

Find that LLVM 15.0.0 has some changes of C API, but I don't see anything affecting this, BuildGlobalStringPtr should be still working

tannergooding commented 2 years ago

LLVM 15 made some changes in how BuildCall and friends work. They are "obsolete" now and you should be using BuildCall2 instead. This requires you to pass in the function type explicitly.

I plan on trying to improve this experience more in the future, but it will require a custom C extension library libLLVMSharp, much as was provided for ClangSharp. Such a library would "fill the gaps" in the official libLLVM C bindings exposed by LLVM.