LLVMSharp icon indicating copy to clipboard operation
LLVMSharp copied to clipboard

LLVM converts integer types to floats

Open AnErrupTion opened this issue 3 years ago • 3 comments

Hey!

I'm making a compiler which will use LLVM as its backend. But while I was doing some tests, I saw that LLVM, for some reason, was transforming any integer types (i8, i16, i32 or i64) to floats (more precisely, ppc_fp128). But some other types like double work and are not converted to floats (since doubles can hold decimals already).

What I don't understand is that I'm not using any decimal values, and it is still converting them into floats, which actually breaks my code.

Here is a sample demo which does this behavior:

unsafe
{
    LLVMModuleRef module = LLVM.ModuleCreateWithName((sbyte*)0);
    LLVMBuilderRef builder = LLVM.CreateBuilder();

    const string targetStr = "x86_64-unknown-none";

    LLVM.InitializeX86TargetInfo();
    LLVM.InitializeX86Target();
    LLVM.InitializeX86TargetMC();
    LLVM.InitializeX86AsmParser();
    LLVM.InitializeX86AsmPrinter();

    module.Target = targetStr;

    var target = LLVMTargetRef.GetTargetFromTriple(targetStr);
    var machine = target.CreateTargetMachine(targetStr, "generic", "", LLVMCodeGenOptLevel.LLVMCodeGenLevelNone,
        LLVMRelocMode.LLVMRelocStatic, LLVMCodeModel.LLVMCodeModelKernel);

    LLVM.SetModuleDataLayout(module, machine.CreateTargetDataLayout());

    var function = module.AddFunction("main", LLVM.FunctionType(LLVM.VoidType(), null, 0, 0));
    var stack = new Stack<LLVMValueRef>();

    LLVM.SetLinkage(function, LLVMLinkage.LLVMExternalLinkage);
    builder.PositionAtEnd(function.AppendBasicBlock("entry"));

    // Simulating what the stack would look like before Add
    stack.Push(LLVM.ConstReal(LLVM.Int32Type(), 8));
    stack.Push(LLVM.ConstReal(LLVM.Int32Type(), 4));

    var value2 = stack.Pop();
    var value1 = stack.Pop();

    stack.Push(builder.BuildAdd(value1, value2));

    fixed (LLVMOpaqueType** ptr = new[] { LLVM.Int32Type() })
    {
        var tmp = module.AddFunction("tmp", LLVM.FunctionType(LLVM.VoidType(), ptr, 1, 0));
        LLVM.SetLinkage(tmp, LLVMLinkage.LLVMExternalLinkage);
        builder.BuildCall(tmp, new[] { stack.Pop() /* This should be the result of the previous Add instruction */ });
    }

    builder.BuildRetVoid();

    LLVM.DumpModule(module);
}

Here is the LLVM IR output:

target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-none"

define void @main() {
entry:
  call void @tmp(ppc_fp128 add (ppc_fp128 0xM40200000000000000000000000000000, ppc_fp128 0xM40100000000000000000000000000000))
  ret void
}

declare void @tmp(i32 %0)

I'm not quite sure if this is a bug, or if I'm using LLVM(Sharp) wrong here. Any help would be greatly appreciated!

AnErrupTion avatar Aug 09 '22 20:08 AnErrupTion

Could you clarify which version of LLVMSharp you're using?

For reference, this is incorrect usage and likely and I'm actually surprised its producing IR at all:

  • LLVMConstReal just calls ConstantFP::get(RealTy, N): https://llvm.org/doxygen/IR_2Core_8cpp_source.html#l01396
  • This in turn ends up calling FV.convert(Ty->getScalarType()->getFltSemantics(), APFloat::rmNearestTiesToEven, &ignored): https://llvm.org/doxygen/Constants_8cpp_source.html#l00926
  • getFltSemantics switches on the type id and should be raising llvm_unreachable("Invalid floating type"): https://llvm.org/doxygen/Type_8cpp_source.html#l00067

LLVMSharp is ultimately fairly simple bindings over LLVM. As such, it is very lowlevel and only really has the safety that libLLVM has built into itself, but should in general work exactly like it does in C.

I'd like to eventually build a higher level safe wrapper on top (much like I did for ClangSharp) and there is some semblance on the start of that with things like: https://github.com/dotnet/LLVMSharp/blob/main/sources/LLVMSharp/LLVMContext.cs, but its not nearly there or close to complete yet.

tannergooding avatar Aug 10 '22 15:08 tannergooding

Could you clarify which version of LLVMSharp you're using?

I'm using LLVMSharp 14.0.0-beta1 from NuGet.

For reference, this is incorrect usage and likely and I'm actually surprised its producing IR at all:

I'm fairly new to LLVM in general, so I just guessed how to use it. And documentation doesn't really help in my case, as I'm not actually making an AST or anything, I'm transforming the bytecode to LLVM IR.

So I guess it's not how you create constant values. So yeah, it doesn't generate any error or anything. For reference (which may or may not help), here is what I get when running llc --version, I get this on the first line:

Ubuntu LLVM version 14.0.0

AnErrupTion avatar Aug 10 '22 16:08 AnErrupTion

Thanks! That all lines up then. I'm unsure why its not erroring, as it really should be.

I'll leave this issue open to track adding some diagnostics on the LLVMSharp side to help catch some of these issues. The raw LLVM.RealConst wouldn't expose them, but I can expose it via LLVMValueRef.CreateConstReal which is a "safer" (but still lowlevel) wrapper over the raw bindings.

tannergooding avatar Aug 10 '22 16:08 tannergooding