Warum erzwingt Clang die Umwandlung von Strukturparametern in Ints?

Wenn Sie Strukturparameter in einer Funktion verwenden, ändert clang die Funktionssignatur. Anstatt einen Strukturtyp zu verwenden, ist die Signatur ein erzwungenes int von gleicher Größe. In meinem Compiler-Projekt verwende ich den Typ llvm struct für die Methodensignatur (was logischer erscheint).

Dies wäre kein Problem, mit Ausnahme der Tatsache, dass die resultierende Assembly, die von LLVM bei Verwendung der Struktur oder der erzwungenen Typen erstellt wird, unterschiedlich ist und nichtanrufkompatibel. Dies führt dazu, dass mein Compiler mit C-Funktionen mit Strukturen nicht ABI-kompatibel ist.

Warum macht clang das? Ist das im C ABI spezifiziert?

Hier ist eine einfache Beispiel-C-Quelldatei:

struct TwoInt { int a, b; };

struct EightChar { char a, b, c, d, e, f, g, h; };

void doTwoInt(struct TwoInt a) {}

void doEightChar(struct EightChar a) {}

int main()
{
        struct TwoInt ti;
        struct EightChar fc;

        doTwoInt(ti);
        doEightChar(fc);

        return 0;
}

Resultierendes LLVM-IR von Clang

%struct.TwoInt = type { i32, i32 }
%struct.EightChar = type { i8, i8, i8, i8, i8, i8, i8, i8 }

define void @doTwoInt(i64 %a.coerce) nounwind uwtable {
  %a = alloca %struct.TwoInt, align 8
  %1 = bitcast %struct.TwoInt* %a to i64*
  store i64 %a.coerce, i64* %1, align 1
  ret void
}

define void @doEightChar(i64 %a.coerce) nounwind uwtable {
  %a = alloca %struct.EightChar, align 8
  %1 = bitcast %struct.EightChar* %a to i64*
  store i64 %a.coerce, i64* %1, align 1
  ret void
}

define i32 @main() nounwind uwtable {
  %1 = alloca i32, align 4
  %ti = alloca %struct.TwoInt, align 4
  %fc = alloca %struct.EightChar, align 1
  store i32 0, i32* %1
  %2 = bitcast %struct.TwoInt* %ti to i64*
  %3 = load i64* %2, align 1
  call void @doTwoInt(i64 %3)
  %4 = bitcast %struct.EightChar* %fc to i64*
  %5 = load i64* %4, align 1
  call void @doEightChar(i64 %5)
  ret i32 0
}

Was ich erwartet hätte (und was mein Compiler ausgibt):

%TwoInt = type { i32, i32 }
%EightChar = type { i8, i8, i8, i8, i8, i8, i8, i8 }

define void @doTwoInt(%TwoInt %a) {
  %1 = alloca i32
  %2 = alloca %TwoInt
  store %TwoInt %a, %TwoInt* %2
  ret void
}

define void @doEightChar(%EightChar %a) {
  %1 = alloca i32
  %2 = alloca %EightChar
  store %EightChar %a, %EightChar* %2
  ret void
}

define i32 @main() {
  %1 = alloca i32
  %ti = alloca %TwoInt
  %fc = alloca %EightChar
  %2 = load %TwoInt* %ti
  call void @doTwoInt(%TwoInt %2)
  %3 = load %EightChar* %fc
  call void @doEightChar(%EightChar %3)
  ret i32 0
}

Antworten auf die Frage(1)

Ihre Antwort auf die Frage