terralang / terra

Terra is a low-level system programming language that is embedded in and meta-programmed by the Lua programming language.

Home Page:terralang.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Calling convention bug in AArch64

elliottslaughter opened this issue · comments

Even after #593 we seem to be hitting issues in the calling convention. This issue is to track status so I don't forget what's going on.

Reproducer 1

local c = terralib.includec("stdio.h")

struct S2 {
  a : int;
  b : double;
  c : double;
}

terra f10(a : S2)
  c.printf("terra: %d %f %f\n", a.a, a.b, a.c)
  return a.a, a.b, a.c
end

terra c10(a : int, b : double, c : double)
  return f10(S2 { a, b, c })
end

local x = f10({1,2.5,3.5})
print(x, x._0, x._1, x._2)
local y = c10(1, 2.5,  3.5)
print(y, y._0, y._1, y._2)
assert(x._0 == 1)
assert(x._1 == 2.5)
assert(x._2 == 3.5)
../build/bin/terra cconv.t

Produces:

terra: 0 0.000000 0.000000
cdata<struct _int32_double_double_>: 0x550b6b1688       0       6.9076310043589e-310    1.8046165208518e-312
terra: 1 2.500000 3.500000
cdata<struct _int32_double_double_>: 0x550b6ab668       1       2.5     3.5
cconv.t:22: assertion failed!

Observations:

  • LuaJIT calls to Terra functions with struct arguments pass bogus data.
  • Can be worked around via a Terra wrapper with scalar arguments (i.e., the Terra calling convention is consistent).

Reproducer 2

#include <stdio.h>

typedef struct S2 {
  int a;
  double b;
  double c;
} S2;

typedef struct R {
  int _0;
  double _1;
  double _2;
} R;

R f10(S2 a) {
  printf("c: %d %f %f\n", a.a, a.b, a.c);
  R r;
  r._0 = a.a;
  r._1 = a.b;
  r._2 = a.c;
  return r;
}
local c = terralib.includecstring [[
#include <stdio.h>

typedef struct S2 {
  int a;
  double b;
  double c;
} S2;

typedef struct R {
  int _0;
  double _1;
  double _2;
} R;

R f10(S2 a);
]]

terralib.linklibrary("libcconv_c.so")

terra c10(x : int, y : double, z : double)
  return c.f10(c.S2 { x, y, z })
end

local x = c10(1, 2.5,  3.5)
print(x, x._0, x._1, x._2)
assert(x._0 == 1)
assert(x._1 == 2.5)
assert(x._2 == 3.5)
cc cconv_c.c -o libcconv_c.so -Wall -shared
LD_LIBRARY_PATH=$PWD ../build/bin/terra cconv_c.t

Produces:

Segmentation fault (core dumped)

Observations:

  • Moving f10 into C results in a crash. Note: the shared library is used to disable inlining.

Reproducer 3

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

typedef struct S2 {
  int a;
  double b;
  double c;
} S2;

typedef struct R {
  int _0;
  double _1;
  double _2;
} R;

R f10(S2 a) {
  printf("c: %d %f %f\n", a.a, a.b, a.c);
  R r;
  r._0 = a.a;
  r._1 = a.b;
  r._2 = a.c;
  return r;
}
]]

terra c10(x : int, y : double, z : double)
  return c.f10(c.S2 { x, y, z })
end

local x = c10(1, 2.5,  3.5)
print(x, x._0, x._1, x._2)
assert(x._0 == 1)
assert(x._1 == 2.5)
assert(x._2 == 3.5)
../build/bin/terra cconv_inline.t

Produces:

c: 1 2.500000 3.500000
cdata<struct R>: 0x550b6ea558   1       2.5     3.5

Observations:

  • Moving the C function back into terralib.includecstring permits inlining, which seems to allow this test to pass.

Reproducer 4

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

typedef struct S2 {
  int a;
  double b;
  double c;
} S2;

typedef struct R {
  int _0;
  double _1;
  double _2;
} R;

__attribute__ ((noinline))
R f10(S2 a) {
  printf("c: %d %f %f\n", a.a, a.b, a.c);
  R r;
  r._0 = a.a;
  r._1 = a.b;
  r._2 = a.c;
  return r;
}
]]

terra c10(x : int, y : double, z : double)
  return c.f10(c.S2 { x, y, z })
end

local x = c10(1, 2.5,  3.5)
print(x, x._0, x._1, x._2)
assert(x._0 == 1)
assert(x._1 == 2.5)
assert(x._2 == 3.5)
../build/bin/terra cconv_noinline.t

Produces:

Segmentation fault (core dumped)

Observation:

  • Inlining can be disabled with __attribute__ ((noinline)), bringing the crash back.

Recommendations

  • Add __attribute__ ((noinline)) to cconv_more.t C generated code to ensure we don't miss any similar bugs in the future.