terralang / terra

Terra is a low-level system programming language that is embedded in and meta-programmed by the Lua programming language.
terralang.org
Other
2.72k stars 201 forks source link

Unable to pass a float to a C function #445

Closed patrick-scho closed 3 years ago

patrick-scho commented 4 years ago

Whenever I try to pass a float to an imported C function, the program just stops. The minimal example for this is:

local C = terralib.includecstring [[
  #include <stdio.h>
]]

terra main()
  C.printf("%f\n", 1f)
end

main()

This never prints for me. When I remove the "f" it runs, but prints 0.000000

patrick-scho commented 4 years ago

I am using the Windows version of the Release 1.0.0-beta2 build

elliottslaughter commented 4 years ago

What happens if you use 1.0 instead of 1f?

I'm thinking this is a type error, %f expects to get a value of type double not float. Though having said that, it's bizarre that it freezes.

patrick-scho commented 4 years ago

Doesn't change anything

elliottslaughter commented 4 years ago

Ok, both programs work for me on macOS.

I may need @ErikMcClure to look at this as it potentially has to do with the changes we made to Windows recently and unfortunately I don't have a Windows dev box.

patrick-scho commented 4 years ago

This also happens, when I try to assign a float/double member in a struct imported from C. The following code runs fine and prints 0.000000 for f1/f2, but when I uncomment the line assigning f1, it stops after printing i1/i2.

local C = terralib.includecstring [[
  #include <stdio.h>

  struct A {
    int i1, i2;
    double f1, f2;
  };

  void print_a(struct A a) {
    printf("i1: %d\ni2: %d\n", a.i1, a.i2);
    printf("f1: %f\nf2: %f\n", a.f1, a.f2);
  }
]]

terra main()
  var a: C.A
  a.i1 = 1
  a.i2 = 4
  --a.f1 = 2.0 -- uncommenting this causes the program to stop after printing i1/i2
  C.print_a(a)
end

main()
ErikMcClure commented 4 years ago

I know that printf, when it encounters a bad argument on windows, will simply abort the program in the same manner that's being described, so it is highly likely that this is some sort of bad argument problem but I have no idea what. I'll have to repro this using a source build somehow.

ErikMcClure commented 4 years ago

@patrick-scho I am unable to reproduce this on Windows 10.0.18363, using Visual Studio 2019 v16.6.0, can you clarify exactly what version of Windows and what version of Visual Studio you are using?

patrick-scho commented 4 years ago

I was using Visual Studio 2019 16.5.2 but I updated to 16.6.0 and the problem remains. I am using Windows 10 Version 1903, Build 18362.836. I downloaded the Linux build as well and ran it under Ubuntu 19.10 in WSL and the same thing happened.

ErikMcClure commented 4 years ago

...are you sure your computer isn't haunted? There is absolutely no way this should happen in Linux.

@aiverson Can you double check that this doesn't happen in linux, and provide a reproducible Nix environment to test it on?

patrick-scho commented 4 years ago

You might just be right, I'm starting to think it actually is haunted, this isn't making any sense. This is enough to make it crash:

local C = terralib.includecstring [[
  #include <stdio.h>

  void test1() {
    printf("%f\n", 1.1);
  }
]]

terra main()
  --C.puts("abc")
  C.test1()
end

main()

On Ubuntu it always prints an error message, under Windows I have to print something before calling test1 so that it prints an error, otherwise it just exits. Just to make sure I compiled the same code directly with a C compiler on both Windows and Ubuntu and it works.

Output on Windows ``` 0 unknown 0x000001f0a04c0038 1 C 0x00007ffc14239179 lua_yield + 62985 2 unknown 0x0000000000010662 3 unknown 0xfffd81f09e000c02 4 unknown 0x000001f09e000c02 5 unknown 0x000001f09e000c02 6 unknown 0x000001f09e000c02 7 unknown 0x000001f09e000c02 8 unknown 0x000001f09e000c02 9 unknown 0xfffd81f0a0000c02 10 unknown 0x000001f09e000c02 11 unknown 0x000001f09e000c02 12 unknown 0x0000000000000c02 13 unknown 0x0000000000000c02 14 unknown 0x0000000000000c02 15 unknown 0x0000000000000c02 16 unknown 0x0000000000000c02 17 unknown 0x0000000000000c02 18 unknown 0x0000000000000c02 19 unknown 0x0000000000000c02 20 unknown 0xfffd81f09e000c02 21 unknown 0xfffd81f09e000c02 22 unknown 0xfffd81f09e000c02 23 unknown 0xfffd81f09e000c02 24 unknown 0x000001f0a0000c02 25 unknown 0x0000000000000c02 26 unknown 0x0000000000000c02 27 unknown 0x0000000000000c02 28 unknown 0x0000000000000c02 29 unknown 0x0000000000000c02 30 unknown 0x0000000000000c02 31 unknown 0xfffd81f09e000c02 32 unknown 0x000001f09e000c02 33 unknown 0x0000000000000c02 34 unknown 0x0000000000000c02 35 unknown 0x0000000000000c02 36 unknown 0x000001f09e000c02 37 unknown 0x0000000000000c02 38 unknown 0x0000000000000c02 39 unknown 0x0000000000000c02 40 unknown 0x0000000000000c02 41 unknown 0x000001f09e000c02 42 unknown 0x0000000000020c02 43 unknown 0x0000000000000c02 44 unknown 0x0000000000000c02 45 unknown 0x0000000000010c02 46 unknown 0x0000000000010c02 ```
Output on Linux ``` 0 terra (JIT) 0x00007fd9bf6b001e $main + 30 terra-Linux-x86_64-1c8dd1b/bin/terra() [0x380c49e] Illegal instruction (core dumped) ```
ErikMcClure commented 4 years ago

Can you try without using the JIT? Use terra.saveobj("out.exe", {main = main}) to generate an executable, then attempt to run the executable directly. I want to see if we can at least narrow this down to weirdness in the JIT (which would explain why a normal C compiler works).

patrick-scho commented 4 years ago

Doesn't make a difference

ErikMcClure commented 4 years ago

I am completely out of ideas. You'll have to get Terra to emit the LLVM IR generated for the EXE at this point.

patrick-scho commented 4 years ago

I have kind of given up already, but maybe it can help. I don't have any experience with this, but I find it strange that there's a definition for printf in the generated IR.

LLVM IR ``` ; ModuleID = 'terra' source_filename = "terra" target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-pc-windows-msvc" %struct._iobuf = type { i8* } %struct.__crt_locale_pointers = type { %struct.__crt_locale_data*, %struct.__crt_multibyte_data* } %struct.__crt_locale_data = type opaque %struct.__crt_multibyte_data = type opaque $printf = comdat any $__local_stdio_printf_options = comdat any @__local_stdio_printf_options._OptionsStorage = internal global i64 0, align 8 @"$string" = private unnamed_addr constant [4 x i8] c"%f\0A\00" ; Function Attrs: nounwind define void @main() local_unnamed_addr #0 { entry: %0 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"$string", i64 0, i64 0), double 1.230000e+00) ret void } ; Function Attrs: inlinehint nounwind define weak_odr i32 @printf(i8* %_Format, ...) local_unnamed_addr #1 comdat { %_ArgList = alloca i8*, align 8 %1 = bitcast i8** %_ArgList to i8* call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %1) #0 call void @llvm.va_start(i8* nonnull %1) %2 = load i8*, i8** %_ArgList, align 8, !tbaa !0 %call = call %struct._iobuf* @__acrt_iob_func(i32 1) #0 %call.i = call i64* @__local_stdio_printf_options() #0 %3 = load i64, i64* %call.i, align 8, !tbaa !4 %call1.i = call i32 @__stdio_common_vfprintf(i64 %3, %struct._iobuf* %call, i8* %_Format, %struct.__crt_locale_pointers* null, i8* %2) #0 call void @llvm.va_end(i8* nonnull %1) call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %1) #0 ret i32 %call1.i } ; Function Attrs: argmemonly nounwind declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #2 ; Function Attrs: nounwind declare void @llvm.va_start(i8*) #0 declare %struct._iobuf* @__acrt_iob_func(i32) local_unnamed_addr #3 ; Function Attrs: noinline norecurse nounwind define weak_odr i64* @__local_stdio_printf_options() local_unnamed_addr #4 comdat { ret i64* @__local_stdio_printf_options._OptionsStorage } declare i32 @__stdio_common_vfprintf(i64, %struct._iobuf*, i8*, %struct.__crt_locale_pointers*, i8*) local_unnamed_addr #3 ; Function Attrs: nounwind declare void @llvm.va_end(i8*) #0 ; Function Attrs: argmemonly nounwind declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #2 attributes #0 = { nounwind } attributes #1 = { inlinehint nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="skylake" "target-features"="+adx,+aes,+avx,+avx2,+bmi,+bmi2,+clflushopt,+cx16,+f16c,+fma,+fsgsbase,+fxsr,+lzcnt,+mmx,+movbe,+mpx,+pclmul,+popcnt,+prfchw,+rdrnd,+rdseed,+rtm,+sahf,+sgx,+sse,+sse2,+sse3,+sse4.1,+sse4.2,+ssse3,+x87,+xsave,+xsavec,+xsaveopt,+xsaves" "unsafe-fp-math"="false" "use-soft-float"="false" } attributes #2 = { argmemonly nounwind } attributes #3 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="skylake" "target-features"="+adx,+aes,+avx,+avx2,+bmi,+bmi2,+clflushopt,+cx16,+f16c,+fma,+fsgsbase,+fxsr,+lzcnt,+mmx,+movbe,+mpx,+pclmul,+popcnt,+prfchw,+rdrnd,+rdseed,+rtm,+sahf,+sgx,+sse,+sse2,+sse3,+sse4.1,+sse4.2,+ssse3,+x87,+xsave,+xsavec,+xsaveopt,+xsaves" "unsafe-fp-math"="false" "use-soft-float"="false" } attributes #4 = { noinline norecurse nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="skylake" "target-features"="+adx,+aes,+avx,+avx2,+bmi,+bmi2,+clflushopt,+cx16,+f16c,+fma,+fsgsbase,+fxsr,+lzcnt,+mmx,+movbe,+mpx,+pclmul,+popcnt,+prfchw,+rdrnd,+rdseed,+rtm,+sahf,+sgx,+sse,+sse2,+sse3,+sse4.1,+sse4.2,+ssse3,+x87,+xsave,+xsavec,+xsaveopt,+xsaves" "unsafe-fp-math"="false" "use-soft-float"="false" } !0 = !{!1, !1, i64 0} !1 = !{!"any pointer", !2, i64 0} !2 = !{!"omnipotent char", !3, i64 0} !3 = !{!"Simple C/C++ TBAA"} !4 = !{!5, !5, i64 0} !5 = !{!"long long", !2, i64 0} ```
ErikMcClure commented 4 years ago

Comparing your LLVM IR to my LLVM IR reveals that dso_local is missing on your LLVM for some reason. I can't figure out what part of LLVM would be adding this in the first place, but it is a weird thing to be missing.

caizongchao commented 4 years ago

terra: 1.0.0 beta2 (https://github.com/terralang/terra/releases/download/release-1.0.0-beta2/terra-Windows-x86_64-1c8dd1b.zip) windows 10 1809

Here the issue I have is that the integer number cannot be printed properly:

> c.printf("hello %d\n", 123)
hello 0

But the float number works without problem:

> c.printf("hello %f\n", 1.23)
hello 1.230000

I also tried terra downloaded from www.luapower.com and it works fine.

I noticed that terra from luapower is built with mingw, it loads the bundled mingw version of stdio.h.

The offical release of terra is built with visual c++, it loads stdio.h from windows sdk.

ErikMcClure commented 4 years ago

This has to be an issue with the specific form of stdio.h being used. Do you know which version of the windows SDK you have installed?

In order to resolve this, I suspect we will have to compare different versions of stdio.h in order to figure out what is triggering this, since it doesn't occur on my machine.

caizongchao commented 4 years ago

The windows sdk version is 10.0.18362.0, which is installed by Visual Studio 2019 Community (16.6.1).

caizongchao commented 4 years ago

I did some more tests regarding this issue. I created a dll which exports a function with var args like printf:

extern "C" {
    __declspec(dllexport) void foo(const char * msg, ...) {
        va_list ap; va_start(ap, msg);

        int n = va_arg(ap, int);

        //vprintf(msg, ap);

        va_end(ap);
    }
}

foo.h:

#pragma once
void foo(const char * msg, ...);

Then call this function in terra:

> c = terralib.includec('foo.h')
> terralib.linklibrary('x64/debug/dlltest.dll')
> c.foo("hello %d\n", 123)

The value of variable n is 0, which should be 123.

I also created a simple exe which calls foo and the value of n is 123 as expected:

typedef void (*foo_ptr)(const char * msg, ...);

int main()
{
    HMODULE h = LoadLibrary(L"/test/dlltest/x64/debug/dlltest.dll");

    foo_ptr foo = (foo_ptr)GetProcAddress(h, "foo");

    foo("hello %d\n", 123);

    return 0;
}
ErikMcClure commented 4 years ago

At least part of this seems to be a JIT/EXE difference, but there could be multiple bugs going on here. Can you create an EXE that calls printf with an integer and see if there's still a problem?

caizongchao commented 4 years ago

The result of calling printf in an exe is correct.

caizongchao commented 4 years ago

I just tried calling printf via luajit ffi, the problem is the same. This might be a problem of luajit.

caizongchao commented 4 years ago

After some investigation I think this might not be a bug. The prototype of printf does not specify the type of the second argument, so luajit passed a float number to printf but printf expects a integer number.

If I change the prototype of printf to int printf(const char * msg, int n) and then the number is printed correctly (both in luajit ffi and terra).

ErikMcClure commented 4 years ago

Okay, so that bug is unrelated but is simply unintuitive behavior caused by variadic functions in C not having type information, so lua type casting can't work and it makes everything into a double.

Sadly we still have no idea what's going on with Patrick.

ghost commented 4 years ago

So won't anyone fix this problem?,I'm also face it with some libraries I tried to bind raylib library to terra,But giving me same output as i'm using Windows

D:\terra\examples>..\bin\terra.exe core_basic_window.t
0   unknown                             0x0000000000000000
1   C                                   0x00007ff9c9519179 lua_yield + 62985
2   unknown                             0x0000000000010661
3   unknown                             0x0000000000000c01
4   unknown                             0x000001bd8c000c01
5   unknown                             0xffffffffff000c01
6   unknown                             0x000001bd8c000c01
7   unknown                             0x0000000000000c01
8   unknown                             0x0000000000000c01
9   unknown                             0x0000000000000c01
10  unknown                             0x0000000000000c01
11  unknown                             0x0000000000000c01
12  unknown                             0x0000000000000c01
13  unknown                             0x0000000000000c01
14  unknown                             0x0000000000000c01
15  unknown                             0x0000000000000c01
16  unknown                             0xfffd81bd8c000c01
17  unknown                             0xfffd81bd8c000c01
18  unknown                             0xfffd81bd8c000c01
19  unknown                             0xfffd81bd8c000c01
20  unknown                             0x000001bd8c000c01
21  unknown                             0x0000000000000c01
22  unknown                             0x0000000000000c01
23  unknown                             0x0000000000000c01
24  unknown                             0x0000000000000c01
25  unknown                             0x0000000000000c01
26  unknown                             0x0000000000000c01
27  unknown                             0xfffd81bd8c000c01
28  unknown                             0x000001bd8c000c01
29  unknown                             0x0000000000000c01
30  unknown                             0x0000000000000c01
31  unknown                             0x0000000000000c01
32  unknown                             0x000001bd8c000c01
33  unknown                             0x0000000000000c01
34  unknown                             0x0000000000000c01
35  unknown                             0x0000000000000c01
36  unknown                             0x0000000000000c01
37  unknown                             0x000001bd8c000c01
38  unknown                             0x0000000000020c01
39  unknown                             0x0000000000000c01
40  unknown                             0x0000000000000c01
41  unknown                             0x0000000000010c01
42  unknown                             0x0000000000010c01
ErikMcClure commented 4 years ago

Can you post what version of windows, what version of visual studio, and what version of the windows SDK you have?

ghost commented 4 years ago

I don't use Visual Studio,I use MinGW but the version of Microsoft Windows i own is 10.0.18362.836 (Maybe this point helps)

The code works 100% in terra compiler,But still facing the bug i mentioned Here is the code if you want to do some investigation:

-- Load raylib libraries
local rl = terralib.includecstring([[
    #include "raylib.h"
    #include "physac.h"
    #include "raygui.h"
    #include "rlgl.h"
    #include "raymath.h"
]])

-- Load C libraries for use
local C = terralib.includecstring([[
    #include "_mingw.h"
    #include "stdio.h"
    #include "stdbool.h"
    #include "stdint.h"
    #include "stdlib.h"
    #include "time.h"
    #include "string.h"
    #include "strings.h"
    #include "unistd.h"
]])

function main()
    rl.InitWindow(0,0,"MadeWithTerra")
    rl.SetTargetFPS(60)
    while not rl.WindowShouldClose() do
        rl.BeginDrawing()
            rl.ClearBackground(rl.RAYWHITE)
            rl.DrawText("TERRA OR TERRABYTE?",10,10,32,rl.BLUE)
        rl.EndDrawing()
    end
    rl.CloseWindow()
    return 0
end
main()
ErikMcClure commented 4 years ago

Right now, Terra only integrates with Visual Studio, not MinGW, and as a result tries to find the Visual Studio headers and the Visual Studio linker using logic borrowed from clang. I have no idea how using MinGW will affect this or if anything compiled with MinGW can actually be used with Terra.

ghost commented 4 years ago

In fact it's will gives same in Visual C++,You know And raylib headers itself can be used on any C/C++ compiler And i don't need to use Visual Studio Question: Why isn't terra compatible with all C and C++ compilers like Tiny C,WATCOM,MinGW,etc...?

ErikMcClure commented 4 years ago

Because it needs to find the C include headers, which are in different locations for all of them. It also needs a linker, which is also in a different location for all of them.

ghost commented 4 years ago

I did added needed headers (In fact i'm also a C programmer) About MinGW,It's always have linking tools That does not makes sense right?

ghost commented 4 years ago

And sorry to say that but,Note that Windows release of Terra does not work cause of missing headers were not added,So i needed to grab them from MinGW and internet

Where all tests failed in fact... But all worked when i added missing headers,See line 11 of code i posted and headers added I can help if you want

ErikMcClure commented 4 years ago

If you are grabbing headers from the internet, Terra won't work. Terra only works if you have an installation of visual studio available, because it needs to be able to find the location of the header files on your computer. Same goes for the linker - it doesn't matter if MinGW ships with a linker. How does Terra know where it is? Magic? A lot of logic is required just to figure out where the C headers or linker is located on a given system, which is why Terra currently only supports Visual Studio.

If you want Terra to support MinGW, start a separate issue for this, but I'm not going to implement it and I don't know anyone else on the project currently working on windows support.

ghost commented 4 years ago

Correction: Most of C/C++ compilers have same headers according to OS Terra does not comes with headers

So i added the missing ones from a copy i have of MinGW,And grabbed just one of them from internet,And all checked

In fact all tests worked that's why i said that output is same and yes,Terra looks for headers on both include and same folders where i run projects of mine

Also i recompiled example more than one time with missing headers,All tests worked successfully and also raylib example,But won't run cause of the issue

If you don't believe me,I suggest you install raylib and create Terra project with code i posted It will works,Just that problem nothing else

Sorry if i'm wrong...

ghost commented 4 years ago

About supporting MinGW,Just the differences are very simple,Just few headers are not work on other compiler,_mingw.h does not work on VC++ and same goes for VC++ where some headers does not works on MinGW

So i do not need even to open issue

ErikMcClure commented 4 years ago

Please open a separate issue, this is not related to passing floats to C functions.

ghost commented 4 years ago

OK

patrick-scho commented 3 years ago

I figured it out. I saw issue #448 and checked whether my CPU supports AVX, turns out it doesn't. Managed to build Terra from source today and after defining DISABLE_AVX the code above runs as expected. I guess that means feature detection needs some improvement but I'm just happy I can finally try out Terra. btw I had some problems building from source, namely that the Clang .libs weren't included in the VS project so I had to add them manually and the DISABLE_AVX define I added in CMake wasn't present in the project either. Not sure if those were my fault though.

elliottslaughter commented 3 years ago

Ok, I'm going to close this issue since it's apparently a duplicate of #448.

Would you mind going into #448 and telling us (a) what LLVM version you used, and (b) whether you built it from source? This could be a deficiency in LLVM's CPU auto-detection and if so, it would be nice to know if they've fixed it in any subsequent versions (e.g., 10, 11).