Skip to content

Commit

Permalink
Fix union alignment (again) and add tests (#288)
Browse files Browse the repository at this point in the history
  • Loading branch information
keynmol authored Jun 23, 2024
1 parent d42916e commit 40ac972
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 21 deletions.
18 changes: 13 additions & 5 deletions modules/bindgen/src/main/scala/analysis/visitStruct.scala
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,13 @@ def visitStruct(cursor: CXCursor, name: String)(using
case un: Def.Union => un.name.value
case st: Def.Struct => st.name.value
case en: Def.Enum => en.name.get.value

info(s"${builder.anonymousFieldStructMapping} -- $nestedName")

val fieldSpec = fieldName -> CType.Reference(
Name.Model(builder.name.value + "." + nestedName)
)

// collector.numAnonymous += 1

builder.anonymousFieldStructMapping.find(
_._2.value == nestedName
) match
Expand All @@ -89,15 +89,23 @@ def visitStruct(cursor: CXCursor, name: String)(using
case st: Def.Struct => st.name.value
val numElements = clang_getArraySize(typ)

builder.fields.addOne(
val fieldSpec =
fieldName -> constArrayType(
CType.Reference(
Name.Model(builder.name.value + "." + nestedName)
),
numElements
)
)
// collector.numAnonymous += 1

builder.anonymousFieldStructMapping.find(
_._2.value == nestedName
) match
case None =>
builder.fields.addOne(fieldSpec)
case Some(idx) =>
builder.fields.update(idx._1, fieldSpec)
builder.anonymousFieldStructMapping.filterInPlace(_ != idx)

case _ =>
builder.fields.addOne(
fieldName -> constructType(clang_getCursorType(cursor))
Expand Down
28 changes: 26 additions & 2 deletions modules/bindgen/src/main/scala/render/struct.scala
Original file line number Diff line number Diff line change
Expand Up @@ -184,13 +184,37 @@ def struct(struct: Def.Struct, line: Appender)(using

alignMethod.foreach(line(_))
line("")

def renderAlignment(tpe: CType): String =
lazy val defaultCase =
s"alignmentof[${scalaType(tpe)}].toInt"
tpe match
case CType.Reference(Name.Model(name, _)) =>
aliasResolver(name) match
case u: CType.Union =>
renderAlignment(u)
case _ =>
defaultCase

case CType.Arr(of, _) => renderAlignment(of)

case CType.Union(fields, _) if fields.nonEmpty =>
fields
.map(t => s"alignmentof[${scalaType(t)}].toInt")
.mkString("Array(", ", ", ").max")
case _ =>
defaultCase
end match
end renderAlignment

namedFieldsWithIndex.foreach { case ((_, fieldType), idx) =>
val tpe = scalaType(fieldType)
if idx == 0 then line(s"res(0) = align(0, alignmentof[$tpe].toInt)")
if idx == 0 then
line(s"res(0) = align(0, ${renderAlignment(fieldType)})")
else
val prevTpe = scalaType(namedFieldsWithIndex(idx - 1)._1._2)
line(
s"res($idx) = align(res(${idx - 1}) + sizeof[$prevTpe].toInt, alignmentof[$tpe].toInt)"
s"res($idx) = align(res(${idx - 1}) + sizeof[$prevTpe].toInt, ${renderAlignment(fieldType)})"
)

}
Expand Down
17 changes: 17 additions & 0 deletions modules/tests/src/test/resources/scala-native/structs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#include "structs.h"
#include <stddef.h>

void write_offsets(int *offsets) {
offsets[0] = offsetof(StructComplexOpaque, p1);
offsets[1] = offsetof(StructComplexOpaque, p2);
offsets[2] = offsetof(StructComplexOpaque, x);
offsets[3] = offsetof(StructComplexOpaque, kiss);
offsets[4] = offsetof(StructComplexOpaque, flag);
offsets[5] = offsetof(StructComplexOpaque, yass);
offsets[6] = offsetof(StructComplexOpaque, bla);
offsets[7] = offsetof(StructComplexOpaque, u1); // sic!
offsets[8] = offsetof(StructComplexOpaque, test);
offsets[9] = offsetof(StructComplexOpaque, flan);
offsets[10] = offsetof(StructComplexOpaque, y);
offsets[11] = offsetof(StructComplexOpaque, top);
}
29 changes: 15 additions & 14 deletions modules/tests/src/test/resources/scala-native/structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,50 +21,51 @@ typedef struct StructComplex {
int HELLO;
} yass;

char* bla;
char *bla;

int test[25];

} StructComplex;

typedef struct StructComplexOpaque {
StructSimple p1, p2;
int x;
StructSimple p1, // 0
p2; // 1
int x; //2

struct {
char yo;
} kiss;
char yo;
} kiss; //3

my_bool flag;
my_bool flag; // 4

struct {
int HELLO;
} yass;
} yass; //5

char* bla;
char *bla; //6

union {
int u0;
long u1;
};
}; //7

int test[25];
int test[25]; // 8

union {
int r0;
long r1;
} flan[2];
} flan[2]; // 9

double y;
double y; // 10

union {
int u0;
long u1;
} top;

} top; // 11

} StructComplexOpaque;

void write_offsets(int *offsets);

typedef struct StructAnonymous {
int x;
Expand Down
16 changes: 16 additions & 0 deletions modules/tests/src/test/scalanative/TestStructs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import scala.scalanative.unsigned.*

class TestStructs:
import lib_test_structs.types.*
import lib_test_structs.functions.*

@Test def test_simple() =
zone {
Expand Down Expand Up @@ -79,6 +80,8 @@ class TestStructs:
assertEquals(my_bool.m_false, struct.flag)
assertEquals(111, struct.x)

assertEquals(struct.y, 480.0, 0.01)

struct.x = 560
assertEquals(560, struct.x)

Expand All @@ -89,6 +92,19 @@ class TestStructs:
assertEquals(struct.yass.HELLO, 5117)

assertEquals(struct.kiss.yo, 'h')

val calculatedOffsets = StructComplexOpaque.offsets
val true_offsets = stackalloc[Int](calculatedOffsets.length)

write_offsets(true_offsets)

val trueOffsets = Array.ofDim[Int](calculatedOffsets.length)

for i <- 0 until calculatedOffsets.length do
trueOffsets(i) = true_offsets(i)

assertArrayEquals(trueOffsets, calculatedOffsets)

}
end test_complex

Expand Down

0 comments on commit 40ac972

Please sign in to comment.